Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

search-select: refactor to use React hooks #124

Merged
merged 20 commits into from Sep 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion devtools/runjson/localdev.json
Expand Up @@ -28,7 +28,7 @@
"Command": [
"./node_modules/.bin/webpack-dev-server",
"--inline",
"--devtool=cheap-module-source-map",
"--devtool=inline-source-map",
mastercactapus marked this conversation as resolved.
Show resolved Hide resolved
"--allowed-hosts=docker.for.mac.host.internal",
"--port=3035",
"--progress=false",
Expand Down
7 changes: 4 additions & 3 deletions service/search.go
Expand Up @@ -3,12 +3,13 @@ package service
import (
"context"
"database/sql"
"strings"
"text/template"

"github.com/target/goalert/permission"
"github.com/target/goalert/search"
"github.com/target/goalert/util/sqlutil"
"github.com/target/goalert/validation/validate"
"strings"
"text/template"

"github.com/pkg/errors"
)
Expand Down Expand Up @@ -58,7 +59,7 @@ var searchTemplate = template.Must(template.New("search").Parse(`
{{end}}
WHERE true
{{if .Omit}}
AND not id = any(:omit)
AND not svc.id = any(:omit)
{{end}}
{{- if and .LabelKey .LabelNegate}}
AND svc.id NOT IN (
Expand Down
7 changes: 4 additions & 3 deletions timezone/search.go
Expand Up @@ -3,12 +3,13 @@ package timezone
import (
"context"
"database/sql"
"strconv"
"text/template"

"github.com/target/goalert/permission"
"github.com/target/goalert/search"
"github.com/target/goalert/util/sqlutil"
"github.com/target/goalert/validation/validate"
"strconv"
"text/template"

"github.com/pkg/errors"
)
Expand Down Expand Up @@ -86,7 +87,7 @@ func (opts renderData) QueryArgs() []sql.NamedArg {
return []sql.NamedArg{
sql.Named("search", opts.SearchStr()),
sql.Named("afterName", opts.After.Name),
sql.Named("omit", sqlutil.UUIDArray(opts.Omit)),
sql.Named("omit", sqlutil.StringArray(opts.Omit)),
}
}

Expand Down
57 changes: 29 additions & 28 deletions web/src/app/alerts/components/AlertsListDataWrapper.js
Expand Up @@ -8,7 +8,7 @@ import Typography from '@material-ui/core/Typography'
import moment from 'moment'
import withStyles from '@material-ui/core/styles/withStyles'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { setCheckedAlerts } from '../../actions'
import { bindActionCreators } from 'redux'
import statusStyles from '../../util/statusStyles'
Expand Down Expand Up @@ -47,7 +47,6 @@ const mapDispatchToProps = dispatch =>
mapStateToProps,
mapDispatchToProps,
)
@withRouter
export default class AlertsListDataWrapper extends Component {
static propTypes = {
alert: p.object.isRequired,
Expand Down Expand Up @@ -98,7 +97,7 @@ export default class AlertsListDataWrapper extends Component {
}

render() {
const { alert, checkedAlerts, classes, history, onServicePage } = this.props
const { alert, checkedAlerts, classes, onServicePage } = this.props

const checkbox = (
<Checkbox
Expand All @@ -114,6 +113,7 @@ export default class AlertsListDataWrapper extends Component {
disableRipple
tabIndex={-1}
onChange={() => this.toggleChecked(alert.number)}
onClick={e => e.stopPropagation()}
/>
)

Expand All @@ -131,34 +131,35 @@ export default class AlertsListDataWrapper extends Component {
}

return (
<ListItem button className={statusClass}>
<ListItem
button
className={statusClass}
component={Link}
to={`/alerts/${alert.number}`}
>
{checkbox}
<div
className={classes.listItem}
onClick={() => history.push(`/alerts/${alert.number}`)}
>
<ListItemText disableTypography style={{ paddingRight: '2.75em' }}>
<Typography>
<b>{alert.number}: </b>
{alert.status.toUpperCase()}
</Typography>
{onServicePage ? null : (
<Typography variant='caption'>{alert.service.name}</Typography>
)}
<Typography variant='caption' noWrap>
{alert.summary}

<ListItemText disableTypography style={{ paddingRight: '2.75em' }}>
<Typography>
<b>{alert.number}: </b>
{alert.status.toUpperCase()}
</Typography>
{onServicePage ? null : (
<Typography variant='caption'>{alert.service.name}</Typography>
)}
<Typography variant='caption' noWrap>
{alert.summary}
</Typography>
</ListItemText>
<ListItemSecondaryAction>
<ListItemText disableTypography>
<Typography variant='caption'>
{moment(alert.created_at)
.local()
.fromNow()}
</Typography>
</ListItemText>
<ListItemSecondaryAction>
<ListItemText disableTypography>
<Typography variant='caption'>
{moment(alert.created_at)
.local()
.fromNow()}
</Typography>
</ListItemText>
</ListItemSecondaryAction>
</div>
</ListItemSecondaryAction>
</ListItem>
)
}
Expand Down
3 changes: 2 additions & 1 deletion web/src/app/apollo.js
Expand Up @@ -88,6 +88,7 @@ const defaultLink = ApolloLink.from([
export const LegacyGraphQLClient = new ApolloClient({
link: defaultLink,
cache: new InMemoryCache(),
defaultOptions: { errorPolicy: 'all' },
})

const graphql2HttpLink = createHttpLink({
Expand Down Expand Up @@ -130,7 +131,7 @@ cache = new InMemoryCache({
},
})

const queryOpts = { fetchPolicy: 'cache-and-network' }
const queryOpts = { fetchPolicy: 'cache-and-network', errorPolicy: 'all' }
if (new URLSearchParams(location.search).get('poll') !== '0') {
queryOpts.pollInterval = POLL_INTERVAL
}
Expand Down
11 changes: 7 additions & 4 deletions web/src/app/dialogs/FormDialog.js
Expand Up @@ -15,6 +15,7 @@ import DialogContentError from './components/DialogContentError'
import { styles as globalStyles } from '../styles/materialStyles'
import gracefulUnmount from '../util/gracefulUnmount'
import { Form } from '../forms'
import ErrorBoundary from '../main/ErrorBoundary'

const styles = theme => {
const { cancelButton, dialogWidth } = globalStyles(theme)
Expand Down Expand Up @@ -131,10 +132,12 @@ export default class FormDialog extends React.PureComponent {
if (valid) onSubmit()
}}
>
{this.renderForm()}
{this.renderCaption()}
{this.renderErrors()}
{this.renderActions()}
<ErrorBoundary>
{this.renderForm()}
{this.renderCaption()}
{this.renderErrors()}
{this.renderActions()}
</ErrorBoundary>
</Form>
</Dialog>
)
Expand Down
91 changes: 44 additions & 47 deletions web/src/app/schedules/ScheduleTZFilter.js
@@ -1,12 +1,12 @@
import React from 'react'
import p from 'prop-types'
import { connect } from 'react-redux'
import { urlParamSelector } from '../selectors'
import { setURLParam } from '../actions'
import Query from '../util/Query'
import gql from 'graphql-tag'
import { FormControlLabel, Switch } from '@material-ui/core'
import { oneOfShape } from '../util/propTypes'
import { useQuery } from 'react-apollo'
import { useSelector, useDispatch } from 'react-redux'

const tzQuery = gql`
query($id: ID!) {
Expand All @@ -17,53 +17,50 @@ const tzQuery = gql`
}
`

@connect(
state => ({ zone: urlParamSelector(state)('tz', 'local') }),
dispatch => ({
setZone: value => dispatch(setURLParam('tz', value, 'local')),
}),
)
export class ScheduleTZFilter extends React.PureComponent {
static propTypes = {
label: p.func,

// one of scheduleID or scheduleTimeZone must be specified
_tz: oneOfShape({
scheduleID: p.string,
scheduleTimeZone: p.string,
}),
export function ScheduleTZFilter(props) {
const params = useSelector(urlParamSelector)
const zone = params('tz', 'local')
const dispatch = useDispatch()
const setZone = value => dispatch(setURLParam('tz', value, 'local'))
const { data, loading, error } = useQuery(tzQuery, {
pollInterval: 0,
variables: { id: props.scheduleID },
})

// provided by connect
zone: p.string,
setZone: p.func,
let label, tz
if (error) {
label = 'Error: ' + (error.message || error)
} else if (loading) {
label = 'Fetching timezone information...'
} else {
tz = data.schedule.timeZone
label = props.label ? props.label(tz) : `Show times in ${tz}`
}
render() {
const { scheduleID, scheduleTimeZone } = this.props
if (scheduleTimeZone) return this.renderControl(scheduleTimeZone)

return (
<Query
variables={{ id: scheduleID }}
query={tzQuery}
noPoll
render={({ data }) => this.renderControl(data.schedule.timeZone)}
/>
)
}
return (
<FormControlLabel
control={
<Switch
checked={zone !== 'local'}
onChange={e => setZone(e.target.checked ? tz : 'local')}
value={tz}
disabled={Boolean(loading || error)}
/>
}
label={label}
/>
)
}
ScheduleTZFilter.propTypes = {
label: p.func,

renderControl(tz) {
const { zone, label, setZone } = this.props
return (
<FormControlLabel
control={
<Switch
checked={zone !== 'local'}
onChange={e => setZone(e.target.checked ? tz : 'local')}
value={tz}
/>
}
label={label ? label(tz) : `Show times in ${tz}`}
/>
)
}
// one of scheduleID or scheduleTimeZone must be specified
_tz: oneOfShape({
scheduleID: p.string,
scheduleTimeZone: p.string,
}),

// provided by connect
zone: p.string,
setZone: p.func,
}
14 changes: 6 additions & 8 deletions web/src/app/selection/EscalationPolicySelect.js
@@ -1,7 +1,5 @@
import React from 'react'

import gql from 'graphql-tag'
import QuerySelect from './QuerySelect'
import { makeQuerySelect } from './QuerySelect'

const query = gql`
query($input: EscalationPolicySearchOptions) {
Expand All @@ -22,8 +20,8 @@ const valueQuery = gql`
}
}
`
export class EscalationPolicySelect extends React.PureComponent {
render() {
return <QuerySelect {...this.props} query={query} valueQuery={valueQuery} />
}
}

export const EscalationPolicySelect = makeQuerySelect(
'EscalationPolicySelect',
{ query, valueQuery },
)
25 changes: 7 additions & 18 deletions web/src/app/selection/LabelKeySelect.js
@@ -1,5 +1,4 @@
import React from 'react'
import QuerySelect from './QuerySelect'
import { makeQuerySelect } from './QuerySelect'
import gql from 'graphql-tag'

const query = gql`
Expand All @@ -12,19 +11,9 @@ const query = gql`
}
`

export class LabelKeySelect extends React.PureComponent {
render() {
return (
<QuerySelect
{...this.props}
variables={{ input: { uniqueKeys: true } }}
defaultQueryVariables={{ input: { uniqueKeys: true } }}
mapDataNode={node => ({
label: node.key,
value: node.key,
})}
query={query}
/>
)
}
}
export const LabelKeySelect = makeQuerySelect('LabelKeySelect', {
variables: { uniqueKeys: true },
defaultQueryVariables: { uniqueKeys: true },
query,
mapDataNode: ({ key }) => ({ label: key, value: key }),
})