mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-05-07 02:32:28 -05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a6fad4db5 | ||
|
|
127bcc14eb | ||
|
|
220d634f80 | ||
|
|
6291af2865 | ||
|
|
e9e808d339 | ||
|
|
780e4a6e9e | ||
|
|
c4bc2f24d6 | ||
|
|
d23ddd0e0b | ||
|
|
4f0ee79409 | ||
|
|
7b07445c5d | ||
|
|
dcbf4bfef8 | ||
|
|
6ec870bb08 | ||
|
|
2c6dbb42c1 | ||
|
|
98725dce04 |
@@ -1,4 +1,5 @@
|
||||
./build
|
||||
./coverage
|
||||
./dist
|
||||
./node_modules
|
||||
./test
|
||||
|
||||
@@ -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
|
||||
|
||||
26
CHANGELOG.md
26
CHANGELOG.md
@@ -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
|
||||
|
||||
27
Dockerfile
27
Dockerfile
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
23
src/common/NotFound.js
Normal 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;
|
||||
@@ -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}>×</button>
|
||||
</h3>
|
||||
<Map center={[ 0, 0 ]} zoom="3">
|
||||
<Map bounds={calculateMapBounds(locations)}>
|
||||
<OpenStreetMapTile />
|
||||
{locations.map(({ cityName, latLong, count }, index) => (
|
||||
<Marker key={index} position={latLong}>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
48
test/common/NotFound.test.js
Normal file
48
test/common/NotFound.test.js
Normal 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');
|
||||
});
|
||||
});
|
||||
13
yarn.lock
13
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user