Compare commits

...

14 Commits

Author SHA1 Message Date
Alejandro Celaya
9a6fad4db5 Merge pull request #110 from acelaya/feature/swipe-map
Prevented side menu to be swipeable while a modal window is displayed
2019-03-03 12:11:28 +01:00
Alejandro Celaya
127bcc14eb Prevented side menu to be swipeable while a modal window is displayed 2019-03-03 12:05:29 +01:00
Alejandro Celaya
220d634f80 Merge pull request #109 from acelaya/feature/map-center
Fixed initial zoom and center on maps
2019-03-03 11:54:44 +01:00
Alejandro Celaya
6291af2865 Fixed initial zoom and center on maps 2019-03-03 11:47:19 +01:00
Alejandro Celaya
e9e808d339 Merge pull request #108 from acelaya/feature/not-found-page
Feature/not found page
2019-03-03 11:24:56 +01:00
Alejandro Celaya
780e4a6e9e Replaced component by render on route rendering not found component with custom props 2019-03-03 11:18:58 +01:00
Alejandro Celaya
c4bc2f24d6 Used not-found component for menu layout inner router 2019-03-03 11:15:34 +01:00
Alejandro Celaya
d23ddd0e0b Created NotFound component 2019-03-03 11:02:29 +01:00
Alejandro Celaya
4f0ee79409 Added missing changelogs 2019-03-03 10:33:37 +01:00
Alejandro Celaya
7b07445c5d Merge pull request #107 from acelaya/feature/improve-docker-image
Feature/improve docker image
2019-03-03 10:28:30 +01:00
Alejandro Celaya
dcbf4bfef8 Added missing docker dependency in travis config 2019-03-03 10:22:26 +01:00
Alejandro Celaya
6ec870bb08 Updated travis so that it tries to build the docker image, whcih in turn builds the project 2019-03-03 10:18:23 +01:00
Alejandro Celaya
2c6dbb42c1 Simplified Dockerfile using multi-stage build 2019-03-03 10:12:59 +01:00
Alejandro Celaya
98725dce04 Added missing array-related dependencies 2019-03-03 10:01:10 +01:00
12 changed files with 151 additions and 35 deletions

View File

@@ -1,4 +1,5 @@
./build
./coverage
./dist
./node_modules
./test

View File

@@ -8,13 +8,16 @@ cache:
directories:
- node_modules
services:
- docker
install:
- yarn install
script:
- yarn lint
- yarn test:ci
- if [[ -z $TRAVIS_TAG ]]; then yarn build ; fi
- if [[ -z $TRAVIS_TAG ]]; then docker build -t shlink-web-client:test . ; fi # Test docker image build only when no tag is present
after_success:
- yarn ocular coverage/clover.xml

View File

@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
## 2.0.1 - 2019-03-03
#### Added
* *Nothing*
#### Changed
* [#106](https://github.com/shlinkio/shlink-web-client/issues/106) Reduced size of docker image by using a multi-stage build Dockerfile.
* [#95](https://github.com/shlinkio/shlink-web-client/issues/95) Tested docker image build during travis executions.
#### Deprecated
* *Nothing*
#### Removed
* *Nothing*
#### Fixed
* [#104](https://github.com/shlinkio/shlink-web-client/issues/104) Fixed blank page being showed when not-found paths are loaded.
* [#94](https://github.com/shlinkio/shlink-web-client/issues/94) Fixed initial zoom and center on maps.
* [#93](https://github.com/shlinkio/shlink-web-client/issues/93) Prevented side menu to be swipeable while a modal window is displayed.
## 2.0.0 - 2019-01-13
#### Added

View File

@@ -1,21 +1,8 @@
FROM nginx:1.15.8-alpine
FROM node:10.15.2 as node
COPY . /shlink-web-client
RUN cd /shlink-web-client && yarn install && yarn build
FROM nginx:1.15.9-alpine
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
# Install node and yarn
RUN apk add --no-cache nodejs && apk add --no-cache yarn
ADD . ./shlink-web-client
# Install dependencies and build project
RUN cd ./shlink-web-client && \
yarn install && \
yarn build && \
# Move build contents to document root
cd .. && \
rm -r /usr/share/nginx/html/* && \
mv ./shlink-web-client/build/* /usr/share/nginx/html && \
rm -r ./shlink-web-client && \
# Delete and uninstall build tools
yarn cache clean && apk del yarn && apk del nodejs
RUN rm -r /usr/share/nginx/html
COPY --from=node /shlink-web-client/build /usr/share/nginx/html

View File

@@ -23,6 +23,9 @@
"@fortawesome/free-regular-svg-icons": "^5.6.3",
"@fortawesome/free-solid-svg-icons": "^5.6.3",
"@fortawesome/react-fontawesome": "^0.1.3",
"array-filter": "^1.0.0",
"array-map": "^0.0.0",
"array-reduce": "^0.0.0",
"axios": "^0.18.0",
"bootstrap": "~4.1.1",
"bottlejs": "^1.7.1",

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import './App.scss';
import NotFound from './common/NotFound';
const App = (MainHeader, Home, MenuLayout, CreateServer) => () => (
<div className="container-fluid app-container">
@@ -11,6 +12,7 @@ const App = (MainHeader, Home, MenuLayout, CreateServer) => () => (
<Route exact path="/server/create" component={CreateServer} />
<Route exact path="/" component={Home} />
<Route path="/server/:serverId" component={MenuLayout} />
<Route component={NotFound} />
</Switch>
</div>
</div>

View File

@@ -6,6 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import classnames from 'classnames';
import * as PropTypes from 'prop-types';
import { serverType } from '../servers/prop-types';
import NotFound from './NotFound';
import './MenuLayout.scss';
const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisits) =>
@@ -38,10 +39,18 @@ const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisi
}
render() {
const { selectedServer } = this.props;
const { selectedServer, match } = this.props;
const { params: { serverId } } = match;
const burgerClasses = classnames('menu-layout__burger-icon', {
'menu-layout__burger-icon--active': this.state.showSideBar,
});
const swipeMenuIfNoModalExists = (showSideBar) => () => {
if (document.querySelector('.modal')) {
return;
}
this.setState({ showSideBar });
};
return (
<React.Fragment>
@@ -54,8 +63,8 @@ const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisi
<Swipeable
delta={40}
className="menu-layout__swipeable"
onSwipedLeft={() => this.setState({ showSideBar: false })}
onSwipedRight={() => this.setState({ showSideBar: true })}
onSwipedLeft={swipeMenuIfNoModalExists(false)}
onSwipedRight={swipeMenuIfNoModalExists(true)}
>
<div className="row menu-layout__swipeable-inner">
<AsideMenu
@@ -88,6 +97,9 @@ const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisi
path="/server/:serverId/manage-tags"
component={TagsList}
/>
<Route
render={() => <NotFound to={`/server/${serverId}/list-short-urls/1`} btnText="List short URLs" />}
/>
</Switch>
</div>
</div>

23
src/common/NotFound.js Normal file
View File

@@ -0,0 +1,23 @@
import React from 'react';
import { Link } from 'react-router-dom';
import * as PropTypes from 'prop-types';
const propTypes = {
to: PropTypes.string,
btnText: PropTypes.string,
};
const NotFound = ({ to = '/', btnText = 'Home' }) => (
<div className="home">
<h2>Oops! We could not find requested route.</h2>
<p>
Use your browser{'\''}s back button to navigate to the page you have previously come from, or just press this button.
</p>
<br />
<Link to={to} className="btn btn-outline-primary btn-lg">{btnText}</Link>
</div>
);
NotFound.propTypes = propTypes;
export default NotFound;

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { Modal, ModalBody } from 'reactstrap';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import { map, prop } from 'ramda';
import * as PropTypes from 'prop-types';
import './MapModal.scss';
@@ -25,6 +26,8 @@ const OpenStreetMapTile = () => (
/>
);
const calculateMapBounds = map(prop('latLong'));
const MapModal = ({ toggle, isOpen, title, locations }) => (
<Modal toggle={toggle} isOpen={isOpen} className="map-modal__modal" contentClassName="map-modal__modal-content">
<ModalBody className="map-modal__modal-body">
@@ -32,7 +35,7 @@ const MapModal = ({ toggle, isOpen, title, locations }) => (
{title}
<button type="button" className="close" onClick={toggle}>&times;</button>
</h3>
<Map center={[ 0, 0 ]} zoom="3">
<Map bounds={calculateMapBounds(locations)}>
<OpenStreetMapTile />
{locations.map(({ cityName, latLong, count }, index) => (
<Marker key={index} position={latLong}>

View File

@@ -19,12 +19,17 @@ describe('<App />', () => {
it('renders app main routes', () => {
const routes = wrapper.find(Route);
const expectedRoutesCount = 3;
const second = 2;
const expectedRoutesCount = 4;
const expectedPaths = [
'/server/create',
'/',
'/server/:serverId',
];
expect.assertions(expectedPaths.length + 1);
expect(routes).toHaveLength(expectedRoutesCount);
expect(routes.at(0).prop('path')).toEqual('/server/create');
expect(routes.at(1).prop('path')).toEqual('/');
expect(routes.at(second).prop('path')).toEqual('/server/:serverId');
expectedPaths.forEach((path, index) => {
expect(routes.at(index).prop('path')).toEqual(path);
});
});
});

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Link } from 'react-router-dom';
import NotFound from '../../src/common/NotFound';
describe('<NotFound />', () => {
let wrapper;
const createWrapper = (props = {}) => {
wrapper = shallow(<NotFound {...props} />);
const content = wrapper.text();
return { wrapper, content };
};
afterEach(() => wrapper && wrapper.unmount());
it('shows expected error title', () => {
const { content } = createWrapper();
expect(content).toContain('Oops! We could not find requested route.');
});
it('shows expected error message', () => {
const { content } = createWrapper();
expect(content).toContain(
'Use your browser\'s back button to navigate to the page you have previously come from, or just press this button.'
);
});
it('shows a link to the home', () => {
const { wrapper } = createWrapper();
const link = wrapper.find(Link);
expect(link.prop('to')).toEqual('/');
expect(link.prop('className')).toEqual('btn btn-outline-primary btn-lg');
expect(link.prop('children')).toEqual('Home');
});
it('shows a link with provided props', () => {
const { wrapper } = createWrapper({ to: '/foo/bar', btnText: 'Hello' });
const link = wrapper.find(Link);
expect(link.prop('to')).toEqual('/foo/bar');
expect(link.prop('className')).toEqual('btn btn-outline-primary btn-lg');
expect(link.prop('children')).toEqual('Hello');
});
});

View File

@@ -1209,9 +1209,10 @@ array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
array-filter@~0.0.0:
version "0.0.1"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
array-filter@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
array-find-index@^1.0.1:
version "1.0.2"
@@ -1236,13 +1237,15 @@ array-includes@^3.0.3:
define-properties "^1.1.2"
es-abstract "^1.7.0"
array-map@~0.0.0:
array-map@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=
array-reduce@~0.0.0:
array-reduce@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=
array-union@^1.0.1:
version "1.0.2"