mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-02-26 11:46:39 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9cef8a029 | ||
|
|
e577eb48d6 | ||
|
|
d08a69954a | ||
|
|
fe81bfccef | ||
|
|
4869435aca | ||
|
|
0822cebb10 | ||
|
|
01a18f2342 | ||
|
|
a22274f382 | ||
|
|
c0098ac7fd | ||
|
|
ba5a99dc2a | ||
|
|
1927ad2d3a | ||
|
|
0356a0204d | ||
|
|
3bf64bee1e | ||
|
|
da484374a1 | ||
|
|
7b9447b717 | ||
|
|
e583eb2759 | ||
|
|
93b4de60f6 | ||
|
|
16f4f7eac8 | ||
|
|
90d4fe72db | ||
|
|
e1298cfa81 | ||
|
|
6be3a1223f | ||
|
|
81d24432a9 |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
jobs:
|
||||
lint:
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- run: npm run lint
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
mutation-tests:
|
||||
continue-on-error: true
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
- run: npm run mutate -- --mutate=$(git diff origin/main --name-only | grep -E 'src\/(.*).(ts|tsx)$' | paste -sd ",")
|
||||
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
2
.github/workflows/docker-image-build.yml
vendored
2
.github/workflows/docker-image-build.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
2
.github/workflows/publish-release.yml
vendored
2
.github/workflows/publish-release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -4,6 +4,27 @@ 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).
|
||||
|
||||
## [3.0.1] - 2020-12-30
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#364](https://github.com/shlinkio/shlink-web-client/issues/364) Fixed all dropdowns so that they are consistently styled.
|
||||
* [#366](https://github.com/shlinkio/shlink-web-client/issues/366) Fixed text in visits menu jumping to next line in some tablet resolutions.
|
||||
* [#367](https://github.com/shlinkio/shlink-web-client/issues/367) Removed conflicting overflow in visits table for mobile devices.
|
||||
* [#365](https://github.com/shlinkio/shlink-web-client/issues/365) Fixed weird rendering of short URLs list in tablets.
|
||||
* [#372](https://github.com/shlinkio/shlink-web-client/issues/372) Fixed importing servers in Android devices.
|
||||
|
||||
|
||||
## [3.0.0] - 2020-12-22
|
||||
### Added
|
||||
* [#340](https://github.com/shlinkio/shlink-web-client/issues/340) Added new "overview" page, showing basic information of the active server.
|
||||
|
||||
384
package-lock.json
generated
384
package-lock.json
generated
@@ -3810,12 +3810,12 @@
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/api": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-4.1.2.tgz",
|
||||
"integrity": "sha512-Oq4RO+FEhcaJciCwM2HuYJaKfnyijmQEGkxCrNK2y5NVTF7lIfTZblLuxEh6+QmTnxuagRxpA+kfgj8j4tmpcw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-4.3.1.tgz",
|
||||
"integrity": "sha512-2eXVXh7yhRML4a6tymx5WU8KmA1BNemIqx7aWnh8Mqx58WSTBfbyipm4JsS7/mzrDLQAguQmqTqsdNJlMkr1eQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mutation-testing-report-schema": "~1.4.3",
|
||||
"mutation-testing-report-schema": "~1.5.2",
|
||||
"surrial": "~2.0.2",
|
||||
"tslib": "~2.0.0"
|
||||
},
|
||||
@@ -3829,18 +3829,18 @@
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/core": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-4.1.2.tgz",
|
||||
"integrity": "sha512-3PgOHHy4IQNP4Gd1yBrLXJQ7CY73Xepzb+LAg4Wdp8pMcjouzTKE+PL+Wjq5AkhF6DjgwF5deCk94GpbikKoPQ==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/core/-/core-4.3.1.tgz",
|
||||
"integrity": "sha512-QjLaO7tvIOlKou6D1iOys46fmbJM42Ps2Wn5hP7G2cBZHB3DgjfYtCSgwF/fgoFybHPukI/8O/2gm302N/jgow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@stryker-mutator/api": "4.1.2",
|
||||
"@stryker-mutator/instrumenter": "4.1.2",
|
||||
"@stryker-mutator/util": "4.1.2",
|
||||
"@stryker-mutator/api": "4.3.1",
|
||||
"@stryker-mutator/instrumenter": "4.3.1",
|
||||
"@stryker-mutator/util": "4.3.1",
|
||||
"ajv": "~6.12.0",
|
||||
"chalk": "~4.1.0",
|
||||
"commander": "~6.2.0",
|
||||
"execa": "~4.0.2",
|
||||
"execa": "~5.0.0",
|
||||
"file-url": "~3.0.0",
|
||||
"get-port": "~5.0.0",
|
||||
"glob": "~7.1.2",
|
||||
@@ -3850,8 +3850,8 @@
|
||||
"log4js": "~6.2.1",
|
||||
"minimatch": "~3.0.4",
|
||||
"mkdirp": "~1.0.3",
|
||||
"mutation-testing-elements": "~1.4.3",
|
||||
"mutation-testing-metrics": "~1.4.3",
|
||||
"mutation-testing-elements": "~1.5.2",
|
||||
"mutation-testing-metrics": "~1.5.2",
|
||||
"npm-run-path": "~4.0.1",
|
||||
"progress": "~2.0.0",
|
||||
"rimraf": "~3.0.0",
|
||||
@@ -3861,70 +3861,9 @@
|
||||
"tree-kill": "~1.2.2",
|
||||
"tslib": "~2.0.0",
|
||||
"typed-inject": "~3.0.0",
|
||||
"typed-rest-client": "~1.7.1"
|
||||
"typed-rest-client": "~1.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"cli-width": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||
"dev": true
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -3937,77 +3876,32 @@
|
||||
}
|
||||
},
|
||||
"execa": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz",
|
||||
"integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz",
|
||||
"integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"get-stream": "^5.0.0",
|
||||
"human-signals": "^1.1.1",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^6.0.0",
|
||||
"human-signals": "^2.1.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^4.0.0",
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"npm-run-path": "^4.0.1",
|
||||
"onetime": "^5.1.2",
|
||||
"signal-exit": "^3.0.3",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
|
||||
"integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==",
|
||||
"dev": true
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.3.3",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
|
||||
"integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.1.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-width": "^3.0.0",
|
||||
"external-editor": "^3.0.3",
|
||||
"figures": "^3.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
"mute-stream": "0.0.8",
|
||||
"run-async": "^2.4.0",
|
||||
"rxjs": "^6.6.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"through": "^2.3.6"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||
"dev": true
|
||||
},
|
||||
"is-stream": {
|
||||
@@ -4016,12 +3910,6 @@
|
||||
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
@@ -4034,12 +3922,6 @@
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||
"dev": true
|
||||
},
|
||||
"npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
@@ -4064,16 +3946,6 @@
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"restore-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
@@ -4083,12 +3955,6 @@
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"run-async": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
|
||||
"dev": true
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -4104,41 +3970,18 @@
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
@@ -4157,9 +4000,9 @@
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/instrumenter": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-4.1.2.tgz",
|
||||
"integrity": "sha512-uPmEPiQ2iMOy/ekxMBOSTqcu3bCjOaD0cCi/VaUqgkuCHUftfFUjk0TP+6whL26QKGfi4Zm9WzNKvswOgmB9yw==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/instrumenter/-/instrumenter-4.3.1.tgz",
|
||||
"integrity": "sha512-NaYopLzD7VrwsryeNDrDEZ3YLXNZXmzQn6A4PukX7t1ETc+4YW0cj0q57IN0UydnYLCEhj6MHmxZAuUZmvTL4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "~7.12.3",
|
||||
@@ -4169,62 +4012,21 @@
|
||||
"@babel/plugin-proposal-decorators": "~7.12.1 ",
|
||||
"@babel/plugin-proposal-private-methods": "^7.12.1",
|
||||
"@babel/preset-typescript": "~7.12.1 ",
|
||||
"@stryker-mutator/api": "4.1.2",
|
||||
"@stryker-mutator/util": "4.1.2",
|
||||
"@stryker-mutator/api": "4.3.1",
|
||||
"@stryker-mutator/util": "4.3.1",
|
||||
"angular-html-parser": "~1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": {
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
|
||||
"integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.12.5",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.5.tgz",
|
||||
"integrity": "sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.12.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.6.tgz",
|
||||
"integrity": "sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/jest-runner": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/jest-runner/-/jest-runner-4.1.2.tgz",
|
||||
"integrity": "sha512-vxj3hUGjAqtCbZTsh+I99Q4xjmD/hXxLU0N7Ey7LXucqfvJErANbVvv+6md0YgpVbQnR9BTl59A09gZFTiEe3g==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/jest-runner/-/jest-runner-4.3.1.tgz",
|
||||
"integrity": "sha512-19jj9mUR92DDBHveT1xOL5i8H2TynJZ5zNR0oFuoco2p8woXNH8CfjMh42NomawD60r4KqupiKAlMAW2f7IsjQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@stryker-mutator/api": "4.1.2",
|
||||
"@stryker-mutator/util": "4.1.2",
|
||||
"semver": "~6.3.0"
|
||||
"@stryker-mutator/api": "4.3.1",
|
||||
"@stryker-mutator/util": "4.3.1",
|
||||
"semver": "~6.3.0",
|
||||
"tslib": "~2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
@@ -4232,32 +4034,56 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/typescript-checker": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/typescript-checker/-/typescript-checker-4.1.2.tgz",
|
||||
"integrity": "sha512-id9mS08OWj89h2lLqXJPCHAPmjoshnrqO8SO3P9k+b+NZ6J5XZf8mLT/xxWOQd1cct6zFBAItEerHpmMeQKL6Q==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/typescript-checker/-/typescript-checker-4.3.1.tgz",
|
||||
"integrity": "sha512-M0VAd9/TNjXwCrL3n4ofihZJN7w1Kd5eEFm4DDs6xrl21gfFspE2sPsLuD7hxToEXN5mJg8jMYXVCZWwakDmXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@stryker-mutator/api": "4.1.2",
|
||||
"@stryker-mutator/util": "4.1.2",
|
||||
"@stryker-mutator/api": "4.3.1",
|
||||
"@stryker-mutator/util": "4.3.1",
|
||||
"semver": "~7.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
|
||||
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@stryker-mutator/util": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-4.1.2.tgz",
|
||||
"integrity": "sha512-b0RuhIMPghNoZqP81aAYsqa0cL/eEkaxEdrrUyZwECIss1Ofg5+KU0SNdjw/VJWNvGsh0tE6rEyjFW+gZJ/TSg==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@stryker-mutator/util/-/util-4.3.1.tgz",
|
||||
"integrity": "sha512-H5XHukO22BLnn0uIl39AUIabTqNTC7rVtrz8Hb1CeGxYF5d5fn5dUGqjUprW9jsxRzt8N3o/j2ysW1ikfB24iA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.flatmap": "~4.5.0"
|
||||
@@ -9199,9 +9025,9 @@
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz",
|
||||
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
|
||||
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
|
||||
"dev": true
|
||||
},
|
||||
"commondir": {
|
||||
@@ -16554,9 +16380,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
@@ -17241,24 +17067,24 @@
|
||||
"dev": true
|
||||
},
|
||||
"mutation-testing-elements": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-1.4.3.tgz",
|
||||
"integrity": "sha512-TxggiswcnNDYh7MI3yZtoAUZocbLDaz95SKHm2YjddUcgglwvc0xv/1GDQ/6UwweRhAISxXi7HpEUFBkSUJQhg==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-elements/-/mutation-testing-elements-1.5.2.tgz",
|
||||
"integrity": "sha512-ugngX+MB6tnwFxirDVSFiCQdbGMLCUQ7oPqltk5QJ/pye8aCyuA90C3Gw8klHk4aRL1JR91FEupacR9CgGXC7w==",
|
||||
"dev": true
|
||||
},
|
||||
"mutation-testing-metrics": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-1.4.3.tgz",
|
||||
"integrity": "sha512-b+5upZuuEDyY5oWb/3Jvo8kMxo9VHVP8t7ryZproBbp9bclDbS3c6kFScR6qJPFQ90NK2S7ab/cGxRHMXnNbJQ==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-metrics/-/mutation-testing-metrics-1.5.2.tgz",
|
||||
"integrity": "sha512-KRMBf1tRNh1snwt+5rZu4Le+dgam+GSX+39WfzJG9k55f/+isRn4hv3dhC4Vl/XdlJ29/Z0dTSe7ZFsWBTABUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mutation-testing-report-schema": "^1.4.3"
|
||||
"mutation-testing-report-schema": "^1.5.2"
|
||||
}
|
||||
},
|
||||
"mutation-testing-report-schema": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-1.4.3.tgz",
|
||||
"integrity": "sha512-+TLMYFGFL8MtcbIF0g9VG+OS5qkPPaZtvyT0v/XWrKgrbAhpnJYNsZV9/xMWuO/V8WC02mLksn/HrDnKnIVqXw==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/mutation-testing-report-schema/-/mutation-testing-report-schema-1.5.2.tgz",
|
||||
"integrity": "sha512-ad90c42vMHa0S4ZZ3e5oZOzGAWg4G8JWto9MrmDkrwInf/Dq+Q8FupCOOTqed0V9FTWqv4sl5arRlYEbedW6Ww==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stream": {
|
||||
@@ -25074,9 +24900,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
@@ -27307,9 +27133,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typed-rest-client": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz",
|
||||
"integrity": "sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA==",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.0.tgz",
|
||||
"integrity": "sha512-Nu1MrdH6ECrRW5gHoRAdubgCs4oH6q5/J76jsEC8bVDfvVoVPkigukPalhMHPwb7ZvpsZqPptd5zpt/QdtrdBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"qs": "^6.9.1",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"repository": "https://github.com/shlinkio/shlink-web-client",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"lint": "npm run lint:js && npm run lint:css",
|
||||
"lint": "npm run lint:css && npm run lint:js",
|
||||
"lint:js": "eslint --ext .js,.ts,.tsx src test scripts config",
|
||||
"lint:js:fix": "npm run lint:js -- --fix",
|
||||
"lint:css": "stylelint src/*.scss src/**/*.scss",
|
||||
@@ -64,9 +64,9 @@
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.12.1",
|
||||
"@shlinkio/eslint-config-js-coding-standard": "~1.1.0",
|
||||
"@stryker-mutator/core": "^4.1.2",
|
||||
"@stryker-mutator/jest-runner": "^4.1.2",
|
||||
"@stryker-mutator/typescript-checker": "^4.1.2",
|
||||
"@stryker-mutator/core": "^4.3.1",
|
||||
"@stryker-mutator/jest-runner": "^4.3.1",
|
||||
"@stryker-mutator/typescript-checker": "^4.3.1",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@types/chart.js": "^2.9.27",
|
||||
"@types/classnames": "^2.2.11",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 MiB After Width: | Height: | Size: 2.3 MiB |
@@ -1,31 +1,12 @@
|
||||
@import '../utils/mixins/vertical-align';
|
||||
|
||||
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn,
|
||||
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn:hover,
|
||||
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn:active {
|
||||
text-align: left;
|
||||
color: #6c757d;
|
||||
border-color: #ced4da;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active,
|
||||
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:hover,
|
||||
.domains-dropdown__toggle-btn--active.domains-dropdown__toggle-btn--active:active {
|
||||
color: #495057;
|
||||
color: #495057 !important;
|
||||
}
|
||||
|
||||
.domains-dropdown__back-btn.domains-dropdown__back-btn,
|
||||
.domains-dropdown__back-btn.domains-dropdown__back-btn:hover {
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.domains-dropdown__toggle-btn.domains-dropdown__toggle-btn::after {
|
||||
right: .75rem;
|
||||
|
||||
@include vertical-align();
|
||||
}
|
||||
|
||||
.domains-dropdown__menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownToggle,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
UncontrolledTooltip,
|
||||
} from 'reactstrap';
|
||||
import { Button, DropdownItem, Input, InputGroup, InputGroupAddon, UncontrolledTooltip } from 'reactstrap';
|
||||
import { InputProps } from 'reactstrap/lib/Input';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||
import { isEmpty, pipe } from 'ramda';
|
||||
import classNames from 'classnames';
|
||||
import { DropdownBtn } from '../utils/DropdownBtn';
|
||||
import { useToggle } from '../utils/helpers/hooks';
|
||||
import { DomainsList } from './reducers/domainsList';
|
||||
import './DomainSelector.scss';
|
||||
@@ -31,7 +21,6 @@ interface DomainSelectorConnectProps extends DomainSelectorProps {
|
||||
|
||||
export const DomainSelector = ({ listDomains, value, domainsList, onChange }: DomainSelectorConnectProps) => {
|
||||
const [ inputDisplayed,, showInput, hideInput ] = useToggle();
|
||||
const [ isDropdownOpen, toggleDropdown ] = useToggle();
|
||||
const { domains } = domainsList;
|
||||
const valueIsEmpty = isEmpty(value);
|
||||
const unselectDomain = () => onChange('');
|
||||
@@ -63,33 +52,24 @@ export const DomainSelector = ({ listDomains, value, domainsList, onChange }: Do
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
) : (
|
||||
<Dropdown isOpen={isDropdownOpen} toggle={toggleDropdown}>
|
||||
<DropdownToggle
|
||||
caret
|
||||
className={classNames(
|
||||
'domains-dropdown__toggle-btn btn-block',
|
||||
{ 'domains-dropdown__toggle-btn--active': !valueIsEmpty },
|
||||
)}
|
||||
>
|
||||
{valueIsEmpty && <>Domain</>}
|
||||
{!valueIsEmpty && <>Domain: {value}</>}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className="domains-dropdown__menu">
|
||||
{domains.map(({ domain, isDefault }) => (
|
||||
<DropdownItem
|
||||
key={domain}
|
||||
active={value === domain || isDefault && valueIsEmpty}
|
||||
onClick={() => onChange(domain)}
|
||||
>
|
||||
{domain}
|
||||
{isDefault && <span className="float-right text-muted">default</span>}
|
||||
</DropdownItem>
|
||||
))}
|
||||
<DropdownItem divider />
|
||||
<DropdownItem onClick={pipe(unselectDomain, showInput)}>
|
||||
<i>New domain</i>
|
||||
<DropdownBtn
|
||||
text={valueIsEmpty ? 'Domain' : `Domain: ${value}`}
|
||||
className={!valueIsEmpty ? 'domains-dropdown__toggle-btn--active' : ''}
|
||||
>
|
||||
{domains.map(({ domain, isDefault }) => (
|
||||
<DropdownItem
|
||||
key={domain}
|
||||
active={value === domain || isDefault && valueIsEmpty}
|
||||
onClick={() => onChange(domain)}
|
||||
>
|
||||
{domain}
|
||||
{isDefault && <span className="float-right text-muted">default</span>}
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
))}
|
||||
<DropdownItem divider />
|
||||
<DropdownItem onClick={pipe(unselectDomain, showInput)}>
|
||||
<i>New domain</i>
|
||||
</DropdownItem>
|
||||
</DropdownBtn>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { CsvJson } from 'csvjson';
|
||||
import { ServerData } from '../data';
|
||||
|
||||
const CSV_MIME_TYPE = 'text/csv';
|
||||
|
||||
interface CsvFile extends File {
|
||||
type: 'text/csv' | 'text/comma-separated-values' | 'application/csv';
|
||||
}
|
||||
|
||||
const CSV_MIME_TYPES = [ 'text/csv', 'text/comma-separated-values', 'application/csv' ];
|
||||
const isCsv = (file?: File | null): file is CsvFile => !!file && CSV_MIME_TYPES.includes(file.type);
|
||||
|
||||
export default class ServersImporter {
|
||||
public constructor(private readonly csvjson: CsvJson, private readonly fileReaderFactory: () => FileReader) {}
|
||||
|
||||
public readonly importServersFromFile = async (file?: File | null): Promise<ServerData[]> => {
|
||||
if (!file || file.type !== CSV_MIME_TYPE) {
|
||||
if (!isCsv(file)) {
|
||||
throw new Error('No file provided or file is not a CSV');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { Card } from 'reactstrap';
|
||||
import Paginator from './Paginator';
|
||||
import { ShortUrlsListProps } from './ShortUrlsList';
|
||||
|
||||
const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (props: ShortUrlsListProps) => {
|
||||
const { match, shortUrlsList } = props;
|
||||
const { match } = props;
|
||||
const { page = '1', serverId = '' } = match?.params ?? {};
|
||||
const { pagination } = shortUrlsList?.shortUrls ?? {};
|
||||
const [ urlsListKey, setUrlsListKey ] = useState(`${serverId}_${page}`);
|
||||
|
||||
// Using a key on a component makes react to create a new instance every time the key changes
|
||||
@@ -18,10 +15,7 @@ const ShortUrls = (SearchBar: FC, ShortUrlsList: FC<ShortUrlsListProps>) => (pro
|
||||
return (
|
||||
<>
|
||||
<div className="form-group"><SearchBar /></div>
|
||||
<Card body className="pb-1">
|
||||
<ShortUrlsList {...props} key={urlsListKey} />
|
||||
<Paginator paginator={pagination} serverId={serverId} />
|
||||
</Card>
|
||||
<ShortUrlsList {...props} key={urlsListKey} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,14 +3,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { head, keys, values } from 'ramda';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Card } from 'reactstrap';
|
||||
import SortingDropdown from '../utils/SortingDropdown';
|
||||
import { determineOrderDir, OrderDir } from '../utils/utils';
|
||||
import { SelectedServer } from '../servers/data';
|
||||
import { isReachableServer, SelectedServer } from '../servers/data';
|
||||
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
|
||||
import { parseQuery } from '../utils/helpers/query';
|
||||
import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList';
|
||||
import { OrderableFields, ShortUrlsListParams, SORTABLE_FIELDS } from './reducers/shortUrlsListParams';
|
||||
import { ShortUrlsTableProps } from './ShortUrlsTable';
|
||||
import Paginator from './Paginator';
|
||||
import './ShortUrlsList.scss';
|
||||
|
||||
interface RouteParams {
|
||||
@@ -40,6 +42,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
|
||||
orderField: orderBy && (head(keys(orderBy)) as OrderableFields),
|
||||
orderDir: orderBy && head(values(orderBy)),
|
||||
});
|
||||
const { pagination } = shortUrlsList?.shortUrls ?? {};
|
||||
const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
|
||||
const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => {
|
||||
setOrder({ orderField, orderDir });
|
||||
@@ -75,7 +78,7 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-block d-md-none mb-3">
|
||||
<div className="d-block d-lg-none mb-3">
|
||||
<SortingDropdown
|
||||
items={SORTABLE_FIELDS}
|
||||
orderField={order.orderField}
|
||||
@@ -83,13 +86,16 @@ const ShortUrlsList = (ShortUrlsTable: FC<ShortUrlsTableProps>) => boundToMercur
|
||||
onChange={handleOrderBy}
|
||||
/>
|
||||
</div>
|
||||
<ShortUrlsTable
|
||||
orderByColumn={orderByColumn}
|
||||
renderOrderIcon={renderOrderIcon}
|
||||
selectedServer={selectedServer}
|
||||
shortUrlsList={shortUrlsList}
|
||||
onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })}
|
||||
/>
|
||||
<Card body className="pb-1">
|
||||
<ShortUrlsTable
|
||||
orderByColumn={orderByColumn}
|
||||
renderOrderIcon={renderOrderIcon}
|
||||
selectedServer={selectedServer}
|
||||
shortUrlsList={shortUrlsList}
|
||||
onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })}
|
||||
/>
|
||||
<Paginator paginator={pagination} serverId={isReachableServer(selectedServer) ? selectedServer.id : ''} />
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}, () => 'https://shlink.io/new-visit');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import '../utils/base';
|
||||
|
||||
.short-urls-table__header {
|
||||
@media (max-width: $smMax) {
|
||||
@media (max-width: $responsiveTableBreakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import '../../utils/mixins/vertical-align';
|
||||
|
||||
.short-urls-row {
|
||||
@media (max-width: $smMax) {
|
||||
@media (max-width: $responsiveTableBreakpoint) {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid $lightGrey;
|
||||
@@ -13,7 +13,7 @@
|
||||
.short-urls-row__cell.short-urls-row__cell {
|
||||
vertical-align: middle !important;
|
||||
|
||||
@media (max-width: $smMax) {
|
||||
@media (max-width: $responsiveTableBreakpoint) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
&:before {
|
||||
content: attr(data-th);
|
||||
font-weight: bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@@ -55,9 +55,10 @@
|
||||
|
||||
.short-urls-row__copy-hint {
|
||||
@include vertical-align(translateX(10px));
|
||||
|
||||
box-shadow: 0 3px 15px rgba(0, 0, 0, .25);
|
||||
|
||||
@media (max-width: $smMax) {
|
||||
@media (max-width: $responsiveTableBreakpoint) {
|
||||
@include vertical-align(translateX(calc(-100% - 20px)));
|
||||
}
|
||||
}
|
||||
|
||||
19
src/utils/DropdownBtn.scss
Normal file
19
src/utils/DropdownBtn.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
@import '../utils/mixins/vertical-align';
|
||||
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled).active,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):active,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):focus,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):hover,
|
||||
.show > .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle {
|
||||
color: #6c757d;
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
border-color: rgba(0, 0, 0, .125);
|
||||
}
|
||||
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:after {
|
||||
@include vertical-align();
|
||||
|
||||
right: .75rem;
|
||||
}
|
||||
22
src/utils/DropdownBtn.tsx
Normal file
22
src/utils/DropdownBtn.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { FC } from 'react';
|
||||
import { Dropdown, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import { useToggle } from './helpers/hooks';
|
||||
import './DropdownBtn.scss';
|
||||
|
||||
export interface DropdownBtnProps {
|
||||
text: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DropdownBtn: FC<DropdownBtnProps> = ({ text, disabled = false, className = '', children }) => {
|
||||
const [ isOpen, toggle ] = useToggle();
|
||||
const toggleClasses = `dropdown-btn__toggle btn-block ${className}`;
|
||||
|
||||
return (
|
||||
<Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled}>
|
||||
<DropdownToggle caret className={toggleClasses} color="primary">{text}</DropdownToggle>
|
||||
<DropdownMenu className="w-100">{children}</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
@@ -28,10 +28,12 @@ export default function SortingDropdown<T extends string = string>(
|
||||
<UncontrolledDropdown>
|
||||
<DropdownToggle
|
||||
caret
|
||||
color={isButton ? 'secondary' : 'link'}
|
||||
className={classNames({ 'btn-block': isButton, 'btn-sm p-0': !isButton })}
|
||||
color={isButton ? 'primary' : 'link'}
|
||||
className={classNames({ 'dropdown-btn__toggle btn-block': isButton, 'btn-sm p-0': !isButton })}
|
||||
>
|
||||
Order by
|
||||
{!isButton && <>Order by</>}
|
||||
{isButton && !orderField && <>Order by...</>}
|
||||
{isButton && orderField && `Order by: "${items[orderField]}" - "${orderDir ?? 'DESC'}"`}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu
|
||||
right={right}
|
||||
|
||||
@@ -7,6 +7,7 @@ $mdMax: 991px;
|
||||
$lgMin: 992px;
|
||||
$lgMax: 1199px;
|
||||
$xlgMin: 1200px;
|
||||
$responsiveTableBreakpoint: $mdMax;
|
||||
|
||||
// Colors
|
||||
$mainColor: #4696e5;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
@import '../../utils/mixins/vertical-align';
|
||||
|
||||
.date-range-selector__btn.date-range-selector__btn,
|
||||
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled).active,
|
||||
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):active,
|
||||
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):focus,
|
||||
.date-range-selector__btn.date-range-selector__btn:not(:disabled):not(.disabled):hover,
|
||||
.show > .date-range-selector__btn.date-range-selector__btn.dropdown-toggle {
|
||||
color: #6c757d;
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
border-color: rgba(0, 0, 0, .125);
|
||||
}
|
||||
|
||||
.date-range-selector__btn.date-range-selector__btn:after {
|
||||
@include vertical-align();
|
||||
|
||||
right: .75rem;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import { useToggle } from '../helpers/hooks';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import { DropdownBtn } from '../DropdownBtn';
|
||||
import {
|
||||
DateInterval,
|
||||
DateRange,
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
rangeIsInterval,
|
||||
} from './types';
|
||||
import DateRangeRow from './DateRangeRow';
|
||||
import './DateRangeSelector.scss';
|
||||
|
||||
export interface DateRangeSelectorProps {
|
||||
initialDateRange?: DateInterval | DateRange;
|
||||
@@ -20,9 +19,8 @@ export interface DateRangeSelectorProps {
|
||||
}
|
||||
|
||||
export const DateRangeSelector = (
|
||||
{ onDatesChange, initialDateRange, defaultText, disabled = false }: DateRangeSelectorProps,
|
||||
{ onDatesChange, initialDateRange, defaultText, disabled }: DateRangeSelectorProps,
|
||||
) => {
|
||||
const [ isOpen, toggle ] = useToggle();
|
||||
const [ activeInterval, setActiveInterval ] = useState(
|
||||
rangeIsInterval(initialDateRange) ? initialDateRange : undefined,
|
||||
);
|
||||
@@ -41,35 +39,30 @@ export const DateRangeSelector = (
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled}>
|
||||
<DropdownToggle caret className="date-range-selector__btn btn-block" color="primary">
|
||||
{rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className="w-100">
|
||||
<DropdownItem
|
||||
active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)}
|
||||
onClick={updateInterval(undefined)}
|
||||
>
|
||||
{defaultText}
|
||||
</DropdownItem>
|
||||
<DropdownItem divider />
|
||||
{([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map(
|
||||
(interval) => (
|
||||
<DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}>
|
||||
{rangeOrIntervalToString(interval)}
|
||||
</DropdownItem>
|
||||
),
|
||||
)}
|
||||
<DropdownItem divider />
|
||||
<DropdownItem header>Custom:</DropdownItem>
|
||||
<DropdownItem text>
|
||||
<DateRangeRow
|
||||
{...activeDateRange}
|
||||
onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })}
|
||||
onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
|
||||
/>
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
<DropdownBtn disabled={disabled} text={rangeOrIntervalToString(activeInterval ?? activeDateRange) ?? defaultText}>
|
||||
<DropdownItem
|
||||
active={activeInterval === undefined && dateRangeIsEmpty(activeDateRange)}
|
||||
onClick={updateInterval(undefined)}
|
||||
>
|
||||
{defaultText}
|
||||
</DropdownItem>
|
||||
<DropdownItem divider />
|
||||
{([ 'today', 'yesterday', 'last7Days', 'last30Days', 'last90Days', 'last180days', 'last365Days' ] as DateInterval[]).map(
|
||||
(interval) => (
|
||||
<DropdownItem key={interval} active={activeInterval === interval} onClick={updateInterval(interval)}>
|
||||
{rangeOrIntervalToString(interval)}
|
||||
</DropdownItem>
|
||||
),
|
||||
)}
|
||||
<DropdownItem divider />
|
||||
<DropdownItem header>Custom:</DropdownItem>
|
||||
<DropdownItem text>
|
||||
<DateRangeRow
|
||||
{...activeDateRange}
|
||||
onStartDateChange={(startDate) => updateDateRange({ ...activeDateRange, startDate })}
|
||||
onEndDateChange={(endDate) => updateDateRange({ ...activeDateRange, endDate })}
|
||||
/>
|
||||
</DropdownItem>
|
||||
</DropdownBtn>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
color: #5d6778;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
|
||||
@media (min-width: $smMin) and (max-width: $mdMax) {
|
||||
font-size: 89%;
|
||||
}
|
||||
}
|
||||
|
||||
.visits-stats__nav-link:hover {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isEmpty, propEq, values } from 'ramda';
|
||||
import { useState, useEffect, useMemo, FC } from 'react';
|
||||
import { Button, Card, Nav, NavLink, Progress } from 'reactstrap';
|
||||
import { Button, Card, Nav, NavLink, Progress, Row } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCalendarAlt, faMapMarkedAlt, faList, faChartPie } from '@fortawesome/free-solid-svg-icons';
|
||||
import { IconDefinition } from '@fortawesome/fontawesome-common-types';
|
||||
@@ -61,11 +61,11 @@ const highlightedVisitsToStats = (
|
||||
let selectedBar: string | undefined;
|
||||
const initialInterval: DateInterval = 'last30Days';
|
||||
|
||||
const VisitsNavLink: FC<VisitsNavLinkProps> = ({ subPath, title, icon, children }) => (
|
||||
const VisitsNavLink: FC<VisitsNavLinkProps & { to: string }> = ({ subPath, title, icon, to }) => (
|
||||
<NavLink
|
||||
tag={RouterNavLink}
|
||||
className="visits-stats__nav-link"
|
||||
to={children}
|
||||
to={to}
|
||||
isActive={(_: null, { pathname }: Location) => pathname.endsWith(`/visits${subPath}`)}
|
||||
replace
|
||||
>
|
||||
@@ -146,12 +146,12 @@ const VisitsStats: FC<VisitsStatsProps> = ({ children, visitsInfo, getVisits, ca
|
||||
return (
|
||||
<>
|
||||
<Card className="visits-stats__nav p-0 overflow-hidden" body>
|
||||
<Nav pills justified>
|
||||
<Nav pills fill>
|
||||
{Object.entries(sections).map(([ section, props ]) =>
|
||||
<VisitsNavLink key={section} {...props}>{buildSectionUrl(props.subPath)}</VisitsNavLink>)}
|
||||
<VisitsNavLink key={section} {...props} to={buildSectionUrl(props.subPath)} />)}
|
||||
</Nav>
|
||||
</Card>
|
||||
<div className="row">
|
||||
<Row>
|
||||
<Switch>
|
||||
<Route exact path={baseUrl}>
|
||||
<div className="col-12 mt-4">
|
||||
@@ -233,7 +233,7 @@ const VisitsStats: FC<VisitsStatsProps> = ({ children, visitsInfo, getVisits, ca
|
||||
|
||||
<Redirect to={baseUrl} />
|
||||
</Switch>
|
||||
</div>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
margin: 1.5rem 0 0;
|
||||
position: relative;
|
||||
background-color: white;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.visits-table__header-cell {
|
||||
|
||||
@@ -6,7 +6,7 @@ module.exports = {
|
||||
tsconfigFile: 'tsconfig.json',
|
||||
testRunner: 'jest',
|
||||
reporters: [ 'progress', 'clear-text' ],
|
||||
coverageAnalysis: 'off',
|
||||
coverageAnalysis: 'perTest',
|
||||
jest: {
|
||||
projectType: 'custom',
|
||||
config: jestConfig,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import { DropdownItem, DropdownMenu, InputGroup } from 'reactstrap';
|
||||
import { DropdownItem, InputGroup } from 'reactstrap';
|
||||
import { DomainSelector } from '../../src/domains/DomainSelector';
|
||||
import { DomainsList } from '../../src/domains/reducers/domainsList';
|
||||
import { ShlinkDomain } from '../../src/api/types';
|
||||
import { DropdownBtn } from '../../src/utils/DropdownBtn';
|
||||
|
||||
describe('<DomainSelector />', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
@@ -23,7 +24,7 @@ describe('<DomainSelector />', () => {
|
||||
|
||||
it('shows dropdown by default', () => {
|
||||
const input = wrapper.find(InputGroup);
|
||||
const dropdown = wrapper.find(DropdownMenu);
|
||||
const dropdown = wrapper.find(DropdownBtn);
|
||||
|
||||
expect(input).toHaveLength(0);
|
||||
expect(dropdown).toHaveLength(1);
|
||||
@@ -33,10 +34,10 @@ describe('<DomainSelector />', () => {
|
||||
it('allows to toggle between dropdown and input', () => {
|
||||
wrapper.find(DropdownItem).last().simulate('click');
|
||||
expect(wrapper.find(InputGroup)).toHaveLength(1);
|
||||
expect(wrapper.find(DropdownMenu)).toHaveLength(0);
|
||||
expect(wrapper.find(DropdownBtn)).toHaveLength(0);
|
||||
|
||||
wrapper.find('.domains-dropdown__back-btn').simulate('click');
|
||||
expect(wrapper.find(InputGroup)).toHaveLength(0);
|
||||
expect(wrapper.find(DropdownMenu)).toHaveLength(1);
|
||||
expect(wrapper.find(DropdownBtn)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,8 +29,12 @@ describe('ServersImporter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('reads file when a CSV is provided', async () => {
|
||||
await importer.importServersFromFile(Mock.of<File>({ type: 'text/csv' }));
|
||||
it.each([
|
||||
[ 'text/csv' ],
|
||||
[ 'text/comma-separated-values' ],
|
||||
[ 'application/csv' ],
|
||||
])('reads file when a CSV is provided', async (type) => {
|
||||
await importer.importServersFromFile(Mock.of<File>({ type }));
|
||||
|
||||
expect(readAsText).toHaveBeenCalledTimes(1);
|
||||
expect(toObject).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { Mock } from 'ts-mockery';
|
||||
import shortUrlsCreator from '../../src/short-urls/ShortUrls';
|
||||
import Paginator from '../../src/short-urls/Paginator';
|
||||
import { ShortUrlsListProps } from '../../src/short-urls/ShortUrlsList';
|
||||
|
||||
describe('<ShortUrls />', () => {
|
||||
@@ -18,9 +17,8 @@ describe('<ShortUrls />', () => {
|
||||
});
|
||||
afterEach(() => wrapper.unmount());
|
||||
|
||||
it('wraps a SearchBar, ShortUrlsList as Paginator', () => {
|
||||
it('wraps a SearchBar and ShortUrlsList', () => {
|
||||
expect(wrapper.find(SearchBar)).toHaveLength(1);
|
||||
expect(wrapper.find(ShortUrlsList)).toHaveLength(1);
|
||||
expect(wrapper.find(Paginator)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
41
test/utils/DropdownBtn.test.tsx
Normal file
41
test/utils/DropdownBtn.test.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { DropdownBtn, DropdownBtnProps } from '../../src/utils/DropdownBtn';
|
||||
|
||||
describe('<DropdownBtn />', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
const createWrapper = (props: PropsWithChildren<DropdownBtnProps>) => {
|
||||
wrapper = shallow(<DropdownBtn {...props} />);
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
afterEach(() => wrapper?.unmount());
|
||||
|
||||
it.each([[ 'foo' ], [ 'bar' ], [ 'baz' ]])('displays provided text', (text) => {
|
||||
const wrapper = createWrapper({ text });
|
||||
const toggle = wrapper.find(DropdownToggle);
|
||||
|
||||
expect(toggle.html()).toContain(text);
|
||||
});
|
||||
|
||||
it.each([[ 'foo' ], [ 'bar' ], [ 'baz' ]])('displays provided children', (children) => {
|
||||
const wrapper = createWrapper({ text: '', children });
|
||||
const menu = wrapper.find(DropdownMenu);
|
||||
|
||||
expect(menu.html()).toContain(children);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[ undefined, 'dropdown-btn__toggle btn-block' ],
|
||||
[ '', 'dropdown-btn__toggle btn-block' ],
|
||||
[ 'foo', 'dropdown-btn__toggle btn-block foo' ],
|
||||
[ 'bar', 'dropdown-btn__toggle btn-block bar' ],
|
||||
])('includes provided classes', (className, expectedClasses) => {
|
||||
const wrapper = createWrapper({ text: '', className });
|
||||
const toggle = wrapper.find(DropdownToggle);
|
||||
|
||||
expect(toggle.prop('className')?.trim()).toEqual(expectedClasses);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import { DropdownItem, DropdownToggle } from 'reactstrap';
|
||||
import { identity, values } from 'ramda';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons';
|
||||
import SortingDropdown, { SortingDropdownProps } from '../../src/utils/SortingDropdown';
|
||||
import { OrderDir } from '../../src/utils/utils';
|
||||
|
||||
describe('<SortingDropdown />', () => {
|
||||
let wrapper: ShallowWrapper;
|
||||
@@ -73,4 +74,23 @@ describe('<SortingDropdown />', () => {
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
expect(onChange).toHaveBeenCalledWith('foo', 'DESC');
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ isButton: false }, '>Order by<' ],
|
||||
[{ isButton: true }, '>Order by...<' ],
|
||||
[
|
||||
{ isButton: true, orderField: 'foo', orderDir: 'ASC' as OrderDir },
|
||||
'Order by: "Foo" - "ASC"',
|
||||
],
|
||||
[
|
||||
{ isButton: true, orderField: 'baz', orderDir: 'DESC' as OrderDir },
|
||||
'Order by: "Hello World" - "DESC"',
|
||||
],
|
||||
[{ isButton: true, orderField: 'baz' }, 'Order by: "Hello World" - "DESC"' ],
|
||||
])('displays expected text in toggle', (props, expectedText) => {
|
||||
const wrapper = createWrapper(props);
|
||||
const toggle = wrapper.find(DropdownToggle);
|
||||
|
||||
expect(toggle.html()).toContain(expectedText);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user