diff --git a/.storybook/preview.js b/.storybook/preview.js index 3f597bebaf7320e9d0f2d5984fcaaf29511fca9f..be0a88b2fbf52b45cb1cc35bf733c9dcba4fda01 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,5 +1,4 @@ import { addDecorator } from '@storybook/react' -import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks' import React from 'react' import { createStore } from 'redux' import { Provider } from 'react-redux' diff --git a/src/client/components/main_layout/TextPage.js b/src/client/components/main_layout/TextPage.js index d6012d5b89237d8208643648f64d321c6640c1cd..6f192bb4a782594981bc39a6fe3e6fc316976b2b 100644 --- a/src/client/components/main_layout/TextPage.js +++ b/src/client/components/main_layout/TextPage.js @@ -29,6 +29,9 @@ const useStyles = makeStyles(theme => ({ } })) +/** + A component for creating a responsive page with static content. + */ const TextPage = props => { const classes = useStyles() return ( diff --git a/src/client/components/main_layout/TextPage.stories.js b/src/client/components/main_layout/TextPage.stories.js index 47442c356e2f4cc74d67802ce96ddabb6ef59901..7d6445bca05408024b524463020b6e0400be0b92 100644 --- a/src/client/components/main_layout/TextPage.stories.js +++ b/src/client/components/main_layout/TextPage.stories.js @@ -6,4 +6,4 @@ export default { title: 'TextPage' } -export const string = () => <TextPage>Some content</TextPage> +export const basic = () => <TextPage>Some content</TextPage> diff --git a/src/client/components/main_layout/TopBar.js b/src/client/components/main_layout/TopBar.js index d5d857ded3f4692caec14fa21b17d41f9bc42dbe..4976fd5c57c153f69c55c4a0ead013bd4956fe95 100644 --- a/src/client/components/main_layout/TopBar.js +++ b/src/client/components/main_layout/TopBar.js @@ -7,7 +7,7 @@ import IconButton from '@material-ui/core/IconButton' import Typography from '@material-ui/core/Typography' import MenuItem from '@material-ui/core/MenuItem' import Menu from '@material-ui/core/Menu' -import { withStyles } from '@material-ui/core/styles' +import { makeStyles } from '@material-ui/core/styles' import MoreIcon from '@material-ui/icons/MoreVert' import Button from '@material-ui/core/Button' import { Link, NavLink } from 'react-router-dom' @@ -19,7 +19,7 @@ import { has } from 'lodash' import secoLogo from '../../img/logos/seco-logo-48x50.png' import { showLanguageButton } from '../../configs/sampo/GeneralConfig' -const styles = theme => ({ +const useStyles = makeStyles((theme) => ({ grow: { flexGrow: 1 }, @@ -67,40 +67,26 @@ const styles = theme => ({ display: 'none' } } -}) +})) -class TopBar extends React.Component { - state = { - infoAnchorEl: null, - mobileMoreAnchorEl: null - }; - - handleInfoMenuOpen = event => { - this.setState({ infoAnchorEl: event.currentTarget }) - }; - - handleInfoMenuClose = () => { - this.setState({ infoAnchorEl: null }) - }; - - handleMobileMenuOpen = event => { - this.setState({ mobileMoreAnchorEl: event.currentTarget }) - }; - - handleMobileMenuClose = () => { - this.setState({ mobileMoreAnchorEl: null }) - }; +const TopBar = props => { + const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null) + const isMobileMenuOpen = Boolean(mobileMoreAnchorEl) + const { perspectives, currentLocale, availableLocales, rootUrl } = props + const classes = useStyles() + const handleMobileMenuOpen = event => setMobileMoreAnchorEl(event.currentTarget) + const handleMobileMenuClose = () => setMobileMoreAnchorEl(null) // https://material-ui.com/components/buttons/#third-party-routing-library - AdapterLink = React.forwardRef((props, ref) => <Link innerRef={ref} {...props} />); - AdapterNavLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />); + const AdapterLink = React.forwardRef((props, ref) => <Link innerRef={ref} {...props} />) + const AdapterNavLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />) - renderMobileMenuItem = perspective => { + const renderMobileMenuItem = perspective => { const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search' if (has(perspective, 'externalUrl')) { return ( <a - className={this.props.classes.link} + className={classes.link} key={perspective.id} href={perspective.externalUrl} target='_blank' @@ -115,8 +101,8 @@ class TopBar extends React.Component { return ( <MenuItem key={perspective.id} - component={this.AdapterLink} - to={`${this.props.rootUrl}/${perspective.id}/${searchMode}`} + component={AdapterLink} + to={`${props.rootUrl}/${perspective.id}/${searchMode}`} > {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()} </MenuItem> @@ -124,19 +110,19 @@ class TopBar extends React.Component { } } - renderDesktopTopMenuItem = perspective => { + const renderDesktopTopMenuItem = perspective => { const searchMode = perspective.id.startsWith('clientFS') ? 'federated-search' : 'faceted-search' if (has(perspective, 'externalUrl')) { return ( <a - className={this.props.classes.link} + className={classes.link} key={perspective.id} href={perspective.externalUrl} target='_blank' rel='noopener noreferrer' > <Button - className={this.props.classes.appBarButton} + className={classes.appBarButton} > {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()} </Button> @@ -146,11 +132,11 @@ class TopBar extends React.Component { return ( <Button key={perspective.id} - className={this.props.classes.appBarButton} - component={this.AdapterNavLink} - to={`${this.props.rootUrl}/${perspective.id}/${searchMode}`} - isActive={(match, location) => location.pathname.startsWith(`${this.props.rootUrl}/${perspective.id}`)} - activeClassName={this.props.classes.appBarButtonActive} + className={classes.appBarButton} + component={AdapterNavLink} + to={`${props.rootUrl}/${perspective.id}/${searchMode}`} + isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/${perspective.id}`)} + activeClassName={classes.appBarButtonActive} > {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()} </Button> @@ -158,32 +144,32 @@ class TopBar extends React.Component { } } - renderMobileMenu = perspectives => + const renderMobileMenu = perspectives => <Menu - anchorEl={this.state.mobileMoreAnchorEl} + anchorEl={mobileMoreAnchorEl} anchorOrigin={{ vertical: 'top', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }} - open={Boolean(this.state.mobileMoreAnchorEl)} - onClose={this.handleMobileMenuClose} + open={isMobileMenuOpen} + onClose={handleMobileMenuClose} > - {perspectives.map(perspective => this.renderMobileMenuItem(perspective))} + {perspectives.map(perspective => renderMobileMenuItem(perspective))} <Divider /> <MenuItem key='feedback' - component={this.AdapterLink} - to={`${this.props.rootUrl}/feedback`} + component={AdapterLink} + to={`${props.rootUrl}/feedback`} > {intl.get('topBar.feedback').toUpperCase()} </MenuItem> <MenuItem key={0} - component={this.AdapterLink} - to={`${this.props.rootUrl}/about`} + component={AdapterLink} + to={`${props.rootUrl}/about`} > {intl.get('topBar.info.aboutThePortal').toUpperCase()} </MenuItem> <a - className={this.props.classes.link} + className={classes.link} key={1} href={intl.get('topBar.info.blogUrl')} target='_blank' @@ -195,85 +181,81 @@ class TopBar extends React.Component { </a> <MenuItem key='info' - component={this.AdapterLink} - to={`${this.props.rootUrl}/instructions`} + component={AdapterLink} + to={`${props.rootUrl}/instructions`} > {intl.get('topBar.instructions').toUpperCase()} </MenuItem> </Menu> - render () { - const { classes, perspectives, currentLocale, availableLocales, rootUrl } = this.props - return ( - <div className={classes.root}> - {/* Add an empty Typography element to ensure that that the MuiTypography class is loaded for + return ( + <div className={classes.root}> + {/* Add an empty Typography element to ensure that that the MuiTypography class is loaded for any lower level components that use MuiTypography class only in translation files */} - <Typography /> - <AppBar position='absolute'> - <Toolbar className={classes.toolbar}> - <Button component={this.AdapterLink} to='/'> - <Typography className={classes.homeButtonText} variant='h6'>{intl.get('appTitle.short')}</Typography> + <Typography /> + <AppBar position='absolute'> + <Toolbar className={classes.toolbar}> + <Button component={AdapterLink} to='/'> + <Typography className={classes.homeButtonText} variant='h6'>{intl.get('appTitle.short')}</Typography> + </Button> + <TopBarSearchField + fetchFullTextResults={props.fetchFullTextResults} + clearResults={props.clearResults} + xsScreen={props.xsScreen} + rootUrl={rootUrl} + /> + <div className={classes.grow} /> + <div className={classes.sectionDesktop}> + {perspectives.map((perspective, index) => renderDesktopTopMenuItem(perspective, index))} + <div className={classes.appBarDivider} /> + <Button + className={classes.appBarButton} + component={AdapterNavLink} + to={`${props.rootUrl}/feedback`} + isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/feedback`)} + activeClassName={classes.appBarButtonActive} + > + {intl.get('topBar.feedback')} </Button> - <TopBarSearchField - fetchFullTextResults={this.props.fetchFullTextResults} - clearResults={this.props.clearResults} - xsScreen={this.props.xsScreen} - rootUrl={rootUrl} - /> - <div className={classes.grow} /> - <div className={classes.sectionDesktop}> - {perspectives.map((perspective, index) => this.renderDesktopTopMenuItem(perspective, index))} - <div className={classes.appBarDivider} /> - <Button - className={classes.appBarButton} - component={this.AdapterNavLink} - to={`${this.props.rootUrl}/feedback`} - isActive={(match, location) => location.pathname.startsWith(`${this.props.rootUrl}/feedback`)} - activeClassName={this.props.classes.appBarButtonActive} - > - {intl.get('topBar.feedback')} - </Button> - <TopBarInfoButton rootUrl={this.props.rootUrl} /> - <Button - className={classes.appBarButton} - component={this.AdapterNavLink} - to={`${this.props.rootUrl}/instructions`} - isActive={(match, location) => location.pathname.startsWith(`${this.props.rootUrl}/instructions`)} - activeClassName={this.props.classes.appBarButtonActive} - > - {intl.get('topBar.instructions')} - </Button> - {showLanguageButton && - <TopBarLanguageButton - currentLocale={currentLocale} - availableLocales={availableLocales} - loadLocales={this.props.loadLocales} - location={this.props.location} - />} - </div> - <a - className={classes.secoLogo} - href='https://seco.cs.aalto.fi' - target='_blank' - rel='noopener noreferrer' + <TopBarInfoButton rootUrl={props.rootUrl} /> + <Button + className={classes.appBarButton} + component={AdapterNavLink} + to={`${props.rootUrl}/instructions`} + isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/instructions`)} + activeClassName={classes.appBarButtonActive} > - <Button><img src={secoLogo} /></Button> - </a> - <div className={classes.sectionMobile}> - <IconButton aria-haspopup='true' onClick={this.handleMobileMenuOpen} color='inherit'> - <MoreIcon /> - </IconButton> - </div> - </Toolbar> - </AppBar> - {this.renderMobileMenu(perspectives)} - </div> - ) - } + {intl.get('topBar.instructions')} + </Button> + {showLanguageButton && + <TopBarLanguageButton + currentLocale={currentLocale} + availableLocales={availableLocales} + loadLocales={props.loadLocales} + location={props.location} + />} + </div> + <a + className={classes.secoLogo} + href='https://seco.cs.aalto.fi' + target='_blank' + rel='noopener noreferrer' + > + <Button><img src={secoLogo} /></Button> + </a> + <div className={classes.sectionMobile}> + <IconButton aria-haspopup='true' onClick={handleMobileMenuOpen} color='inherit'> + <MoreIcon /> + </IconButton> + </div> + </Toolbar> + </AppBar> + {renderMobileMenu(perspectives)} + </div> + ) } TopBar.propTypes = { - classes: PropTypes.object.isRequired, fetchFullTextResults: PropTypes.func.isRequired, clearResults: PropTypes.func.isRequired, loadLocales: PropTypes.func.isRequired, @@ -282,7 +264,10 @@ TopBar.propTypes = { availableLocales: PropTypes.array.isRequired, xsScreen: PropTypes.bool.isRequired, location: PropTypes.object.isRequired, + /** + * Root url of the application. + */ rootUrl: PropTypes.string.isRequired } -export default withStyles(styles)(TopBar) +export default TopBar diff --git a/src/client/components/main_layout/TopBar.stories.js b/src/client/components/main_layout/TopBar.stories.js new file mode 100644 index 0000000000000000000000000000000000000000..c8a7ff4edad74c1ab7daa64df24d42ddee56ccaa --- /dev/null +++ b/src/client/components/main_layout/TopBar.stories.js @@ -0,0 +1,28 @@ +import React from 'react' +import TopBar from './TopBar' +import { useSelector } from 'react-redux' +import { useLocation } from 'react-router-dom' +import { perspectiveConfig } from '../../configs/sampo/PerspectiveConfig' + +export default { + component: TopBar, + title: 'TopBar' +} + +export const basic = props => { + const fullTextSearch = useSelector(state => state.fullTextSearch) + const options = useSelector(state => state.options) + const location = useLocation() + // console.log(props) + return ( + <TopBar + rootUrl='' + search={fullTextSearch} + perspectives={perspectiveConfig} + currentLocale={options.currentLocale} + availableLocales={options.availableLocales} + xsScreen={false} + location={location} + /> + ) +} diff --git a/src/client/components/main_layout/TopBarSearchField.js b/src/client/components/main_layout/TopBarSearchField.js index 210a8406c923bab51f6973ee1b2dd8219b2fb1af..7225d0875a7e1f158907a575eb23a0072c61d56a 100644 --- a/src/client/components/main_layout/TopBarSearchField.js +++ b/src/client/components/main_layout/TopBarSearchField.js @@ -112,7 +112,7 @@ class TopBarSearchField extends React.Component { TopBarSearchField.propTypes = { classes: PropTypes.object.isRequired, - fetchFullTextResults: PropTypes.func.isRequired, + fetchFullTextResults: PropTypes.func, xsScreen: PropTypes.bool.isRequired, rootUrl: PropTypes.string.isRequired }