Files
shields/frontend/components/main.js
Marcin Mielnicki eeb78ccf15 Badge suggestion feature fix (#3331)
* Display suggested badges

* E2e test for badge suggestion

* Suggest resource returns example with pattern

* Do not require preview in MarkupModalContent

* Skip integration test for suggestion

* Unmodifiable path in customizer

* Use suggested link

* Allow to change suggested badges

* Enable skipped test

* Enable skipped test

* Code refactoring

* Code refactoring

* Code refactoring

* Code refactoring

* Code refactoring

* Code refactoring

* Unused code removed

* Unused code removed

* getExampleWithServiceByPattern helper added

* BadgeExamples uses examples instead of services definitions

* Revert "getExampleWithServiceByPattern helper added"

This reverts commit 80839fd705.

* style removed from example

* example.exact replaced with preview.buildFromExample

* keywords are required again

* Code refactoring

* More e2e tests for suggestion feature

* Code refactoring

* Build add with a base url

* showActualParams -> isPrefilled

* A new schema for BadgeExamples

* Link moved to queryParams

* Updated documentation for the suggest reponse format

* Link moved to queryParams - another test updated

* Revert "Link moved to queryParams - another test updated"

This reverts commit b5f811bb07.

* Revert "Link moved to queryParams"

This reverts commit 3b54c6d2b4.

* Disable changes in path in suggested badges

* 'link' element documentation restored
2019-04-29 18:38:27 +02:00

191 lines
5.0 KiB
JavaScript

import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import groupBy from 'lodash.groupby'
import {
categories,
findCategory,
services,
getDefinitionsForCategory,
} from '../lib/service-definitions'
import ServiceDefinitionSetHelper from '../lib/service-definitions/service-definition-set-helper'
import { baseUrl } from '../constants'
import Meta from './meta'
import Header from './header'
import SuggestionAndSearch from './suggestion-and-search'
import DonateBox from './donate'
import MarkupModal from './markup-modal'
import Usage from './usage'
import Footer from './footer'
import {
CategoryHeading,
CategoryHeadings,
CategoryNav,
} from './category-headings'
import BadgeExamples from './badge-examples'
import { BaseFont, GlobalStyle } from './common'
const AppContainer = styled(BaseFont)`
text-align: center;
`
export default class Main extends React.Component {
constructor(props) {
super(props)
this.state = {
isSearchInProgress: false,
isQueryTooShort: false,
searchResults: undefined,
selectedExample: undefined,
}
this.searchTimeout = 0
this.handleExampleSelected = this.handleExampleSelected.bind(this)
this.dismissMarkupModal = this.dismissMarkupModal.bind(this)
this.searchQueryChanged = this.searchQueryChanged.bind(this)
}
static propTypes = {
// `pageContext` is the `context` passed to `createPage()` in
// `gatsby-node.js`. In the case of the index page, `pageContext` is empty.
pageContext: {
category: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}),
}.isRequired,
}
performSearch(query) {
const isQueryTooShort = query.length === 1
let searchResults
if (query.length >= 2) {
const flat = ServiceDefinitionSetHelper.create(services)
.notDeprecated()
.search(query)
.toArray()
searchResults = groupBy(flat, 'category')
}
this.setState({
isSearchInProgress: false,
isQueryTooShort,
searchResults,
})
}
searchQueryChanged(query) {
/*
Add a small delay before showing search results
so that we wait until the user has stopped typing
before we start loading stuff.
This
a) reduces the amount of badges we will load and
b) stops the page from 'flashing' as the user types, like this:
https://user-images.githubusercontent.com/7288322/42600206-9b278470-85b5-11e8-9f63-eb4a0c31cb4a.gif
*/
this.setState({ isSearchInProgress: true })
window.clearTimeout(this.searchTimeout)
this.searchTimeout = window.setTimeout(() => this.performSearch(query), 500)
}
handleExampleSelected(example) {
this.setState({ selectedExample: example })
}
dismissMarkupModal() {
this.setState({ selectedExample: undefined })
}
renderCategory(category, definitions) {
const { id } = category
const flattened = definitions
.reduce((accum, current) => {
const { examples } = current
return accum.concat(examples)
}, [])
.map(({ title, link, example, preview, documentation }) => ({
title,
link,
example,
preview,
documentation,
}))
return (
<div key={id}>
<CategoryHeading category={category} />
<BadgeExamples
baseUrl={baseUrl}
examples={flattened}
onClick={this.handleExampleSelected}
/>
</div>
)
}
renderMain() {
const {
pageContext: { category },
} = this.props
const { isSearchInProgress, isQueryTooShort, searchResults } = this.state
if (isSearchInProgress) {
return <div>searching...</div>
} else if (isQueryTooShort) {
return <div>Search term must have 2 or more characters</div>
} else if (searchResults) {
return Object.entries(searchResults).map(([categoryId, definitions]) =>
this.renderCategory(findCategory(categoryId), definitions)
)
} else if (category) {
const definitions = ServiceDefinitionSetHelper.create(
getDefinitionsForCategory(category.id)
)
.notDeprecated()
.toArray()
return (
<div>
<CategoryNav categories={categories} />
{this.renderCategory(category, definitions)}
</div>
)
} else {
return <CategoryHeadings categories={categories} />
}
}
render() {
const { selectedExample } = this.state
return (
<AppContainer id="app">
<GlobalStyle />
<Meta />
<Header />
<MarkupModal
baseUrl={baseUrl}
example={selectedExample}
onRequestClose={this.dismissMarkupModal}
/>
<section>
<SuggestionAndSearch
baseUrl={baseUrl}
onBadgeClick={this.handleExampleSelected}
queryChanged={this.searchQueryChanged}
/>
<DonateBox />
</section>
{this.renderMain()}
<Usage baseUrl={baseUrl} />
<Footer baseUrl={baseUrl} />
</AppContainer>
)
}
}