From 13c681dc398f9b5788a1ad5808422a79dba6ae67 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 28 Jan 2021 23:09:09 +0100 Subject: [PATCH 01/18] Added first bits of the dark theme styles --- src/App.scss | 12 ++++++++++++ src/common/AsideMenu.scss | 6 +++--- src/index.scss | 19 +++++++++++-------- src/servers/ServersListGroup.scss | 2 +- src/utils/DropdownBtn.scss | 2 +- src/utils/base.scss | 14 +++++++++++--- src/visits/VisitsTable.scss | 2 +- 7 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/App.scss b/src/App.scss index a6566e27..4a4f3b38 100644 --- a/src/App.scss +++ b/src/App.scss @@ -24,3 +24,15 @@ padding: 0 15px; } } + +html:not([data-theme='dark']) { + --primary-color: #{$lightPrimaryColor}; + --secondary-color: #{$lightSecondaryColor}; + --text-color: #{$lightTextColor}; +} + +html[data-theme='dark'] { + --primary-color: #{$darkPrimaryColor}; + --secondary-color: #{$darkSecondaryColor}; + --text-color: #{$darkTextColor}; +} diff --git a/src/common/AsideMenu.scss b/src/common/AsideMenu.scss index 09e0dc3e..60a48c1a 100644 --- a/src/common/AsideMenu.scss +++ b/src/common/AsideMenu.scss @@ -3,7 +3,7 @@ .aside-menu { width: $asideMenuWidth; - background-color: white; + background-color: var(--primary-color); box-shadow: rgba(0, 0, 0, .05) 0 8px 15px; position: fixed !important; padding-top: 13px; @@ -18,7 +18,7 @@ @media (min-width: $mdMin) { padding: 30px 15px 15px; - border-right: 1px solid #eeeeee; + border-right: 1px solid rgba(0, 0, 0, .07); } @media (max-width: $smMax) { @@ -50,7 +50,7 @@ } .aside-menu__item:hover { - background-color: $lightColor; + background-color: var(--secondary-color); } .aside-menu__item--selected { diff --git a/src/index.scss b/src/index.scss index d5542c25..9e5ee63b 100644 --- a/src/index.scss +++ b/src/index.scss @@ -2,15 +2,16 @@ @import 'node_modules/bootstrap/scss/bootstrap.scss'; @import './common/react-tagsinput.scss'; +* { + outline: none !important; +} + html, body, #root { height: 100%; - background: $lightColor; -} - -* { - outline: none !important; + background: var(--secondary-color); + color: var(--text-color); } .bg-main { @@ -21,8 +22,10 @@ body, box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075); } -.card-header { - background-color: white; +.card, +.card-header, +.card-body { + background-color: var(--primary-color); } .card-footer { @@ -56,7 +59,7 @@ body, } .table-hover tbody tr:hover { - background-color: $lightColor; + background-color: var(--secondary-color); } .react-datepicker__input-container, diff --git a/src/servers/ServersListGroup.scss b/src/servers/ServersListGroup.scss index 78457102..e51a58b6 100644 --- a/src/servers/ServersListGroup.scss +++ b/src/servers/ServersListGroup.scss @@ -18,7 +18,7 @@ } .servers-list__server-item:hover { - background-color: $lightColor; + background-color: var(--secondary-color); } .servers-list__server-item-icon { diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss index 6cfae1dc..2327d45b 100644 --- a/src/utils/DropdownBtn.scss +++ b/src/utils/DropdownBtn.scss @@ -7,7 +7,7 @@ .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; + background-color: var(--primary-color); text-align: left; border-color: rgba(0, 0, 0, .125); } diff --git a/src/utils/base.scss b/src/utils/base.scss index 8b70dc17..dfb8b476 100644 --- a/src/utils/base.scss +++ b/src/utils/base.scss @@ -16,6 +16,14 @@ $lightGrey: #dddddd; $dangerColor: #dc3545; $mediumGrey: #dee2e6; +// Themes +$lightPrimaryColor: #ffffff; +$lightSecondaryColor: $lightColor; +$lightTextColor: #212529; +$darkPrimaryColor: #161b22; +$darkSecondaryColor: #0d1117; +$darkTextColor: #ffffff; + // Misc $headerHeight: 57px; $asideMenuWidth: 260px; @@ -23,6 +31,6 @@ $footer-height: 2.3rem; $footer-margin: .8rem; // Bootstrap overwrites -//$theme-colors: ( -// 'primary': $mainColor -//); +$theme-colors: ( + 'primary': $mainColor +); diff --git a/src/visits/VisitsTable.scss b/src/visits/VisitsTable.scss index a8ad3f24..2b20f59c 100644 --- a/src/visits/VisitsTable.scss +++ b/src/visits/VisitsTable.scss @@ -4,7 +4,7 @@ .visits-table { margin: 1.5rem 0 0; position: relative; - background-color: white; + background-color: var(--primary-color); overflow-y: hidden; } From 53f16ac8b5a3d57ccd718a1cab521602fd404493 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 28 Jan 2021 23:15:42 +0100 Subject: [PATCH 02/18] Added primary color alfa and tables color --- src/App.scss | 2 ++ src/common/AsideMenu.scss | 1 - src/common/MenuLayout.scss | 2 +- src/index.scss | 6 +++++- src/short-urls/Paginator.scss | 2 +- src/utils/base.scss | 3 +++ 6 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/App.scss b/src/App.scss index 4a4f3b38..ca211d43 100644 --- a/src/App.scss +++ b/src/App.scss @@ -27,12 +27,14 @@ html:not([data-theme='dark']) { --primary-color: #{$lightPrimaryColor}; + --primary-color-alfa: #{$lightPrimaryColorAlfa}; --secondary-color: #{$lightSecondaryColor}; --text-color: #{$lightTextColor}; } html[data-theme='dark'] { --primary-color: #{$darkPrimaryColor}; + --primary-color-alfa: #{$darkPrimaryColorAlfa}; --secondary-color: #{$darkSecondaryColor}; --text-color: #{$darkTextColor}; } diff --git a/src/common/AsideMenu.scss b/src/common/AsideMenu.scss index 60a48c1a..d92eccf6 100644 --- a/src/common/AsideMenu.scss +++ b/src/common/AsideMenu.scss @@ -18,7 +18,6 @@ @media (min-width: $mdMin) { padding: 30px 15px 15px; - border-right: 1px solid rgba(0, 0, 0, .07); } @media (max-width: $smMax) { diff --git a/src/common/MenuLayout.scss b/src/common/MenuLayout.scss index 7eae132d..63f476c5 100644 --- a/src/common/MenuLayout.scss +++ b/src/common/MenuLayout.scss @@ -22,7 +22,7 @@ z-index: 1035; font-size: 1.5rem; cursor: pointer; - color: rgba(255, 255, 255, .5); + color: var(--primary-color-alfa); @media (max-width: $smMax) { display: inline-block; diff --git a/src/index.scss b/src/index.scss index 9e5ee63b..f4835f92 100644 --- a/src/index.scss +++ b/src/index.scss @@ -29,7 +29,7 @@ body, } .card-footer { - background-color: rgba(255, 255, 255, .5); + background-color: var(--primary-color-alfa); } .container-xl { @@ -58,6 +58,10 @@ body, background-color: $mainColor; } +.table { + color: var(--text-color); +} + .table-hover tbody tr:hover { background-color: var(--secondary-color); } diff --git a/src/short-urls/Paginator.scss b/src/short-urls/Paginator.scss index 03b784d0..fbee7e23 100644 --- a/src/short-urls/Paginator.scss +++ b/src/short-urls/Paginator.scss @@ -1,7 +1,7 @@ .short-urls-paginator { position: sticky; bottom: 0; - background-color: rgba(255, 255, 255, .5); + background-color: var(--primary-color-alfa); padding: .75rem 0; border-top: 1px solid rgba(black, .125); } diff --git a/src/utils/base.scss b/src/utils/base.scss index dfb8b476..62870475 100644 --- a/src/utils/base.scss +++ b/src/utils/base.scss @@ -18,9 +18,12 @@ $mediumGrey: #dee2e6; // Themes $lightPrimaryColor: #ffffff; +$lightPrimaryColorAlfa: rgba($lightPrimaryColor, .5); $lightSecondaryColor: $lightColor; $lightTextColor: #212529; + $darkPrimaryColor: #161b22; +$darkPrimaryColorAlfa: rgba(#161b22, .5); $darkSecondaryColor: #0d1117; $darkTextColor: #ffffff; From f313a39b8183f3ea986ebb7bb46991f8f1a6a0c8 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 15 Feb 2021 23:45:13 +0100 Subject: [PATCH 03/18] Added brand color and input styles to dark theme --- src/App.scss | 14 ----- src/common/AsideMenu.scss | 8 +-- src/common/MainHeader.scss | 2 +- src/common/react-tagsinput.scss | 10 +++- src/index.scss | 98 +++++++++++++++++++++++---------- src/servers/Overview.scss | 2 +- src/short-urls/Paginator.scss | 2 +- src/tags/TagCard.scss | 6 +- src/theme/theme.scss | 47 ++++++++++++++++ src/utils/DateInput.scss | 17 ++++++ src/utils/DropdownBtn.scss | 4 +- src/utils/base.scss | 13 +---- src/visits/VisitsStats.scss | 2 +- 13 files changed, 152 insertions(+), 73 deletions(-) create mode 100644 src/theme/theme.scss diff --git a/src/App.scss b/src/App.scss index ca211d43..a6566e27 100644 --- a/src/App.scss +++ b/src/App.scss @@ -24,17 +24,3 @@ padding: 0 15px; } } - -html:not([data-theme='dark']) { - --primary-color: #{$lightPrimaryColor}; - --primary-color-alfa: #{$lightPrimaryColorAlfa}; - --secondary-color: #{$lightSecondaryColor}; - --text-color: #{$lightTextColor}; -} - -html[data-theme='dark'] { - --primary-color: #{$darkPrimaryColor}; - --primary-color-alfa: #{$darkPrimaryColorAlfa}; - --secondary-color: #{$darkSecondaryColor}; - --text-color: #{$darkTextColor}; -} diff --git a/src/common/AsideMenu.scss b/src/common/AsideMenu.scss index d92eccf6..35b0339e 100644 --- a/src/common/AsideMenu.scss +++ b/src/common/AsideMenu.scss @@ -52,14 +52,10 @@ background-color: var(--secondary-color); } -.aside-menu__item--selected { - color: #ffffff; - background-color: $mainColor; -} - +.aside-menu__item--selected, .aside-menu__item--selected:hover { color: #ffffff; - background-color: $mainColor; + background-color: var(--brand-color); } .aside-menu__item--divider { diff --git a/src/common/MainHeader.scss b/src/common/MainHeader.scss index 89279c68..03d9ce80 100644 --- a/src/common/MainHeader.scss +++ b/src/common/MainHeader.scss @@ -1,8 +1,8 @@ @import '../utils/base'; .main-header.main-header { - background-color: $mainColor !important; color: white; + background-color: var(--brand-color) !important; .navbar-brand { color: inherit !important; diff --git a/src/common/react-tagsinput.scss b/src/common/react-tagsinput.scss index 6753a0f9..54a5facb 100644 --- a/src/common/react-tagsinput.scss +++ b/src/common/react-tagsinput.scss @@ -1,6 +1,6 @@ .react-tagsinput { - background-color: #ffffff; - border: 1px solid #cccccc; + background-color: var(--input-color); + border: 1px solid var(--input-border-color); border-radius: .25rem; overflow: hidden; min-height: 2.6rem; @@ -10,7 +10,7 @@ .react-tagsinput--focused { border-color: #80bdff; - box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25); + box-shadow: 0 0 0 .2rem rgb(70 150 229 / 25%); } .react-tagsinput-tag { @@ -46,3 +46,7 @@ font-size: 1.25rem; color: #495057; } + +.react-autosuggest__suggestion--highlighted { + background-color: var(--active-color); +} diff --git a/src/index.scss b/src/index.scss index f4835f92..691c537e 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,6 +1,7 @@ @import './utils/base'; @import 'node_modules/bootstrap/scss/bootstrap.scss'; @import './common/react-tagsinput.scss'; +@import './theme/theme'; * { outline: none !important; @@ -14,24 +15,69 @@ body, color: var(--text-color); } +@media (min-width: $smMin) { + #root { + transition: filter 300ms; + } + + .modal-open #root { + filter: blur(1.5px); + } +} + .bg-main { background-color: $mainColor !important; } -.card { - box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075); -} - -.card, +.card-body, .card-header, -.card-body { - background-color: var(--primary-color); +.list-group-item { + background-color: transparent; } .card-footer { background-color: var(--primary-color-alfa); } +.card { + box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075); + background-color: var(--primary-color); +} + +.modal-content, +.page-link, +.page-item.disabled .page-link, +.dropdown-menu { + background-color: var(--primary-color); +} + +.modal-header, +.modal-footer, +.card-header, +.card-footer, +.table thead th, +.table th, +.table td, +.page-link, +.page-link:hover, +.page-item.disabled .page-link, +.dropdown-divider { + border-color: var(--border-color); +} + +.page-link:hover { + background-color: var(--secondary-color); +} + +.page-item.active .page-link { + background-color: var(--brand-color); + border-color: var(--brand-color); +} + +.pagination .page-link { + cursor: pointer; +} + .container-xl { @media (min-width: $xlgMin) { max-width: 1320px; @@ -43,22 +89,29 @@ body, } } +.dropdown-item { + color: var(--text-color); +} + .dropdown-item:not(:disabled) { cursor: pointer; } +.dropdown-item:focus:not(:disabled), +.dropdown-item:hover:not(:disabled), .dropdown-item.active:not(:disabled), .dropdown-item:active:not(:disabled) { - background-color: $lightGrey !important; - color: inherit !important; + background-color: var(--active-color) !important; + color: var(--text-color) !important; } .badge-main { color: #ffffff; - background-color: $mainColor; + background-color: var(--brand-color); } -.table { +.table, +.table-hover tbody tr:hover { color: var(--text-color); } @@ -66,13 +119,10 @@ body, background-color: var(--secondary-color); } -.react-datepicker__input-container, -.react-datepicker-wrapper { - display: block !important; -} - -.react-datepicker-popper { - z-index: 2; +.form-control, +.form-control:focus { + background-color: var(--input-color); + border-color: var(--input-border-color); } .navbar-brand { @@ -81,10 +131,6 @@ body, } } -.pagination .page-link { - cursor: pointer; -} - .indivisible { white-space: nowrap; } @@ -99,14 +145,6 @@ body, white-space: nowrap; } -.react-datepicker__day--keyboard-selected { - background-color: $mainColor; - - &:hover { - background-color: darken($mainColor, 12%); - } -} - .progress-bar { background-color: $mainColor; } diff --git a/src/servers/Overview.scss b/src/servers/Overview.scss index 75a56ec4..d7f4dba2 100644 --- a/src/servers/Overview.scss +++ b/src/servers/Overview.scss @@ -2,7 +2,7 @@ .overview__card.overview__card { text-align: center; - border-top: 3px solid $mainColor; + border-top: 3px solid var(--brand-color); } .overview__card-title { diff --git a/src/short-urls/Paginator.scss b/src/short-urls/Paginator.scss index fbee7e23..b28b471a 100644 --- a/src/short-urls/Paginator.scss +++ b/src/short-urls/Paginator.scss @@ -3,5 +3,5 @@ bottom: 0; background-color: var(--primary-color-alfa); padding: .75rem 0; - border-top: 1px solid rgba(black, .125); + border-top: 1px solid var(--border-color); } diff --git a/src/tags/TagCard.scss b/src/tags/TagCard.scss index ce6849aa..ecb79abe 100644 --- a/src/tags/TagCard.scss +++ b/src/tags/TagCard.scss @@ -1,3 +1,5 @@ +@import '../utils/base'; + .tag-card.tag-card { margin-bottom: .5rem; } @@ -26,11 +28,11 @@ } .tag-card__tag-name { - color: #007bff; + color: $mainColor; cursor: pointer; } .tag-card__tag-name:hover { - color: #0056b3; + color: darken($mainColor, 15%); text-decoration: underline; } diff --git a/src/theme/theme.scss b/src/theme/theme.scss new file mode 100644 index 00000000..5d9dd194 --- /dev/null +++ b/src/theme/theme.scss @@ -0,0 +1,47 @@ +@import '../utils/base'; + +// Light theme colors +$lightPrimaryColor: #ffffff; +$lightPrimaryColorAlfa: rgba($lightPrimaryColor, .5); +$lightSecondaryColor: $lightColor; +$lightTextColor: #212529; +$lightBorderColor: rgba(0, 0, 0, .125); +$lightActiveColor: $lightGrey; +$lightBrandColor: $mainColor; +$lightInputColor: $lightPrimaryColor; +$lightBorderInputColor: rgba(0, 0, 0, .19); + +// Dark theme colors +$darkPrimaryColor: #161b22; +$darkPrimaryColorAlfa: rgba($darkPrimaryColor, .7); +$darkSecondaryColor: #0f131a; +$darkTextColor: rgb(201, 209, 217); +$darkBorderColor: rgba(0, 0, 0, .2); +$darkActiveColor: $darkSecondaryColor; +$darkBrandColor: #0b2d4e; +$darkInputColor: darken($darkPrimaryColor, 2%); +$darkBorderInputColor: rgba(0, 0, 0, .3); + +html:not([data-theme='dark']) { + --primary-color: #{$lightPrimaryColor}; + --primary-color-alfa: #{$lightPrimaryColorAlfa}; + --secondary-color: #{$lightSecondaryColor}; + --text-color: #{$lightTextColor}; + --border-color: #{$lightBorderColor}; + --active-color: #{$lightActiveColor}; + --brand-color: #{$lightBrandColor}; + --input-color: #{$lightInputColor}; + --input-border-color: #{$lightBorderInputColor}; +} + +html[data-theme='dark'] { + --primary-color: #{$darkPrimaryColor}; + --primary-color-alfa: #{$darkPrimaryColorAlfa}; + --secondary-color: #{$darkSecondaryColor}; + --text-color: #{$darkTextColor}; + --border-color: #{$darkBorderColor}; + --active-color: #{$darkActiveColor}; + --brand-color: #{$darkBrandColor}; + --input-color: #{$darkInputColor}; + --input-border-color: #{$darkBorderInputColor}; +} diff --git a/src/utils/DateInput.scss b/src/utils/DateInput.scss index 9653c38b..ee27d849 100644 --- a/src/utils/DateInput.scss +++ b/src/utils/DateInput.scss @@ -32,3 +32,20 @@ background-color: #333333; font-size: 14px; } + +.react-datepicker__input-container, +.react-datepicker-wrapper { + display: block !important; +} + +.react-datepicker-popper { + z-index: 2; +} + +.react-datepicker__day--keyboard-selected { + background-color: $mainColor; + + &:hover { + background-color: darken($mainColor, 12%); + } +} diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss index 2327d45b..d81c18fe 100644 --- a/src/utils/DropdownBtn.scss +++ b/src/utils/DropdownBtn.scss @@ -7,9 +7,9 @@ .dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):hover, .show > .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle { color: #6c757d; - background-color: var(--primary-color); + background-color: var(--input-color); text-align: left; - border-color: rgba(0, 0, 0, .125); + border-color: var(--input-border-color); } .dropdown-btn__toggle.dropdown-btn__toggle:after { diff --git a/src/utils/base.scss b/src/utils/base.scss index 62870475..ff82f97f 100644 --- a/src/utils/base.scss +++ b/src/utils/base.scss @@ -12,21 +12,10 @@ $responsiveTableBreakpoint: $mdMax; // Colors $mainColor: #4696e5; $lightColor: #f5f6fe; -$lightGrey: #dddddd; +$lightGrey: #eeeeee; $dangerColor: #dc3545; $mediumGrey: #dee2e6; -// Themes -$lightPrimaryColor: #ffffff; -$lightPrimaryColorAlfa: rgba($lightPrimaryColor, .5); -$lightSecondaryColor: $lightColor; -$lightTextColor: #212529; - -$darkPrimaryColor: #161b22; -$darkPrimaryColorAlfa: rgba(#161b22, .5); -$darkSecondaryColor: #0d1117; -$darkTextColor: #ffffff; - // Misc $headerHeight: 57px; $asideMenuWidth: 260px; diff --git a/src/visits/VisitsStats.scss b/src/visits/VisitsStats.scss index dc945d12..1caafc6d 100644 --- a/src/visits/VisitsStats.scss +++ b/src/visits/VisitsStats.scss @@ -25,6 +25,6 @@ .visits-stats__nav-link.active { border-color: $mainColor; - background-color: white !important; + background-color: var(--primary-color) !important; color: $mainColor !important; } From 9dbf790cc8bbf905d0daf3409cb9eee670e4130c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 16 Feb 2021 19:25:23 +0100 Subject: [PATCH 04/18] Added components and logic to dynamically change theme --- src/App.tsx | 9 ++++++-- src/container/index.ts | 2 +- src/settings/RealTimeUpdates.tsx | 3 +++ src/settings/Settings.tsx | 11 +++++++--- src/settings/UserInterface.tsx | 26 ++++++++++++++++++++++++ src/settings/reducers/settings.ts | 14 +++++++++++++ src/settings/services/provideServices.ts | 14 +++++++++++-- src/utils/theme/index.ts | 8 ++++++++ test/App.test.tsx | 4 +++- test/settings/reducers/settings.test.ts | 12 ++++++++++- 10 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 src/settings/UserInterface.tsx diff --git a/src/App.tsx b/src/App.tsx index 4928f42d..23938617 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,11 +2,14 @@ import { useEffect, FC } from 'react'; import { Route, Switch } from 'react-router-dom'; import NotFound from './common/NotFound'; import { ServersMap } from './servers/data'; +import { Settings } from './settings/reducers/settings'; +import { changeThemeInMarkup } from './utils/theme'; import './App.scss'; interface AppProps { fetchServers: Function; servers: ServersMap; + settings: Settings; } const App = ( @@ -17,12 +20,14 @@ const App = ( EditServer: FC, Settings: FC, ShlinkVersionsContainer: FC, -) => ({ fetchServers, servers }: AppProps) => { - // On first load, try to fetch the remote servers if the list is empty +) => ({ fetchServers, servers, settings }: AppProps) => { useEffect(() => { + // On first load, try to fetch the remote servers if the list is empty if (Object.keys(servers).length === 0) { fetchServers(); } + + changeThemeInMarkup(settings.ui?.theme ?? 'light'); }, []); return ( diff --git a/src/container/index.ts b/src/container/index.ts index f7a0d3cf..b369c1f8 100644 --- a/src/container/index.ts +++ b/src/container/index.ts @@ -43,7 +43,7 @@ bottle.serviceFactory( 'Settings', 'ShlinkVersionsContainer', ); -bottle.decorator('App', connect([ 'servers' ], [ 'fetchServers' ])); +bottle.decorator('App', connect([ 'servers', 'settings' ], [ 'fetchServers' ])); provideCommonServices(bottle, connect, withRouter); provideApiServices(bottle); diff --git a/src/settings/RealTimeUpdates.tsx b/src/settings/RealTimeUpdates.tsx index 914c5a6f..d737f6e9 100644 --- a/src/settings/RealTimeUpdates.tsx +++ b/src/settings/RealTimeUpdates.tsx @@ -19,6 +19,9 @@ const RealTimeUpdates = ( Enable or disable real-time updates, when using Shlink v2.2.0 or newer. + + Real-time updates are currently being {realTimeUpdates.enabled ? 'processed' : 'ignored'}. + diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index bbd524d9..31e6fc6f 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -2,14 +2,19 @@ import { FC } from 'react'; import { Row } from 'reactstrap'; import NoMenuLayout from '../common/NoMenuLayout'; -const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC) => () => ( +const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC, UserInterface:FC) => () => (
- +
+ +
+
+ +
- +
diff --git a/src/settings/UserInterface.tsx b/src/settings/UserInterface.tsx new file mode 100644 index 00000000..89597320 --- /dev/null +++ b/src/settings/UserInterface.tsx @@ -0,0 +1,26 @@ +import { FC } from 'react'; +import { SimpleCard } from '../utils/SimpleCard'; +import ToggleSwitch from '../utils/ToggleSwitch'; +import { changeThemeInMarkup, Theme } from '../utils/theme'; +import { Settings, UiSettings } from './reducers/settings'; + +interface UserInterfaceProps { + settings: Settings; + setUiSettings: (settings: UiSettings) => void; +} + +export const UserInterface: FC = ({ settings: { ui }, setUiSettings }) => ( + + { + const theme: Theme = useDarkTheme ? 'dark' : 'light'; + + setUiSettings({ theme }); + changeThemeInMarkup(theme); + }} + > + Use dark theme + + +); diff --git a/src/settings/reducers/settings.ts b/src/settings/reducers/settings.ts index aa1bc929..e5c0d1f9 100644 --- a/src/settings/reducers/settings.ts +++ b/src/settings/reducers/settings.ts @@ -2,6 +2,7 @@ import { Action } from 'redux'; import { dissoc, mergeDeepRight } from 'ramda'; import { buildReducer } from '../../utils/helpers/redux'; import { RecursivePartial } from '../../utils/utils'; +import { Theme } from '../../utils/theme'; export const SET_SETTINGS = 'shlink/realTimeUpdates/SET_SETTINGS'; @@ -19,9 +20,14 @@ export interface ShortUrlCreationSettings { validateUrls: boolean; } +export interface UiSettings { + theme: Theme; +} + export interface Settings { realTimeUpdates: RealTimeUpdatesSettings; shortUrlCreation?: ShortUrlCreationSettings; + ui?: UiSettings; } const initialState: Settings = { @@ -31,6 +37,9 @@ const initialState: Settings = { shortUrlCreation: { validateUrls: false, }, + ui: { + theme: 'light', + }, }; type SettingsAction = Action & Settings; @@ -55,3 +64,8 @@ export const setShortUrlCreationSettings = (settings: ShortUrlCreationSettings): type: SET_SETTINGS, shortUrlCreation: settings, }); + +export const setUiSettings = (settings: UiSettings): PartialSettingsAction => ({ + type: SET_SETTINGS, + ui: settings, +}); diff --git a/src/settings/services/provideServices.ts b/src/settings/services/provideServices.ts index 393ccefc..cd01599b 100644 --- a/src/settings/services/provideServices.ts +++ b/src/settings/services/provideServices.ts @@ -1,14 +1,20 @@ import Bottle from 'bottlejs'; import RealTimeUpdates from '../RealTimeUpdates'; import Settings from '../Settings'; -import { setRealTimeUpdatesInterval, setShortUrlCreationSettings, toggleRealTimeUpdates } from '../reducers/settings'; +import { + setRealTimeUpdatesInterval, + setShortUrlCreationSettings, + setUiSettings, + toggleRealTimeUpdates, +} from '../reducers/settings'; import { ConnectDecorator } from '../../container/types'; import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServer'; import { ShortUrlCreation } from '../ShortUrlCreation'; +import { UserInterface } from '../UserInterface'; const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Components - bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates', 'ShortUrlCreation'); + bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates', 'ShortUrlCreation', 'UserInterface'); bottle.decorator('Settings', withoutSelectedServer); bottle.decorator('Settings', connect(null, [ 'resetSelectedServer' ])); @@ -21,10 +27,14 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('ShortUrlCreation', () => ShortUrlCreation); bottle.decorator('ShortUrlCreation', connect([ 'settings' ], [ 'setShortUrlCreationSettings' ])); + bottle.serviceFactory('UserInterface', () => UserInterface); + bottle.decorator('UserInterface', connect([ 'settings' ], [ 'setUiSettings' ])); + // Actions bottle.serviceFactory('toggleRealTimeUpdates', () => toggleRealTimeUpdates); bottle.serviceFactory('setRealTimeUpdatesInterval', () => setRealTimeUpdatesInterval); bottle.serviceFactory('setShortUrlCreationSettings', () => setShortUrlCreationSettings); + bottle.serviceFactory('setUiSettings', () => setUiSettings); }; export default provideServices; diff --git a/src/utils/theme/index.ts b/src/utils/theme/index.ts index a4b5f96b..ca56555b 100644 --- a/src/utils/theme/index.ts +++ b/src/utils/theme/index.ts @@ -5,3 +5,11 @@ export const MAIN_COLOR_ALPHA = 'rgba(70, 150, 229, 0.4)'; export const HIGHLIGHTED_COLOR = '#F77F28'; export const HIGHLIGHTED_COLOR_ALPHA = 'rgba(247, 127, 40, 0.4)'; + +export type Theme = 'dark' | 'light'; + +export const changeThemeInMarkup = (theme: Theme) => { + const html = document.getElementsByTagName('html'); + + html?.[0]?.setAttribute('data-theme', theme); +}; diff --git a/test/App.test.tsx b/test/App.test.tsx index 26938298..da1fda0b 100644 --- a/test/App.test.tsx +++ b/test/App.test.tsx @@ -1,6 +1,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Route } from 'react-router-dom'; import { identity } from 'ramda'; +import { Mock } from 'ts-mockery'; +import { Settings } from '../src/settings/reducers/settings'; import appFactory from '../src/App'; describe('', () => { @@ -10,7 +12,7 @@ describe('', () => { beforeEach(() => { const App = appFactory(MainHeader, () => null, () => null, () => null, () => null, () => null, () => null); - wrapper = shallow(); + wrapper = shallow(()} />); }); afterEach(() => wrapper.unmount()); diff --git a/test/settings/reducers/settings.test.ts b/test/settings/reducers/settings.test.ts index 1bfd1701..57d22066 100644 --- a/test/settings/reducers/settings.test.ts +++ b/test/settings/reducers/settings.test.ts @@ -3,12 +3,14 @@ import reducer, { toggleRealTimeUpdates, setRealTimeUpdatesInterval, setShortUrlCreationSettings, + setUiSettings, } from '../../../src/settings/reducers/settings'; describe('settingsReducer', () => { const realTimeUpdates = { enabled: true }; const shortUrlCreation = { validateUrls: false }; - const settings = { realTimeUpdates, shortUrlCreation }; + const ui = { theme: 'light' }; + const settings = { realTimeUpdates, shortUrlCreation, ui }; describe('reducer', () => { it('returns realTimeUpdates when action is SET_SETTINGS', () => { @@ -39,4 +41,12 @@ describe('settingsReducer', () => { expect(result).toEqual({ type: SET_SETTINGS, shortUrlCreation: { validateUrls: true } }); }); }); + + describe('setUiSettings', () => { + it('creates action to set ui settings', () => { + const result = setUiSettings({ theme: 'dark' }); + + expect(result).toEqual({ type: SET_SETTINGS, ui: { theme: 'dark' } }); + }); + }); }); From 7f6c71e8d7afebf6dfbe3c8634ebde6d3617bc96 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 16 Feb 2021 19:31:16 +0100 Subject: [PATCH 05/18] Created UserInterface test --- src/settings/UserInterface.tsx | 2 +- test/settings/ShortUrlCreation.test.tsx | 2 +- test/settings/UserInterface.test.tsx | 47 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/settings/UserInterface.test.tsx diff --git a/src/settings/UserInterface.tsx b/src/settings/UserInterface.tsx index 89597320..608cf65f 100644 --- a/src/settings/UserInterface.tsx +++ b/src/settings/UserInterface.tsx @@ -20,7 +20,7 @@ export const UserInterface: FC = ({ settings: { ui }, setUiS changeThemeInMarkup(theme); }} > - Use dark theme + Use dark theme. ); diff --git a/test/settings/ShortUrlCreation.test.tsx b/test/settings/ShortUrlCreation.test.tsx index 07d0bd7b..1cb92d9a 100644 --- a/test/settings/ShortUrlCreation.test.tsx +++ b/test/settings/ShortUrlCreation.test.tsx @@ -25,7 +25,7 @@ describe('', () => { [{ validateUrls: true }, true ], [{ validateUrls: false }, false ], [ undefined, false ], - ])('switch is toggled if option is tru', (shortUrlCreation, expectedChecked) => { + ])('switch is toggled if option is true', (shortUrlCreation, expectedChecked) => { const wrapper = createWrapper(shortUrlCreation); const toggle = wrapper.find(ToggleSwitch); diff --git a/test/settings/UserInterface.test.tsx b/test/settings/UserInterface.test.tsx new file mode 100644 index 00000000..e5c295e3 --- /dev/null +++ b/test/settings/UserInterface.test.tsx @@ -0,0 +1,47 @@ +import { shallow, ShallowWrapper } from 'enzyme'; +import { Mock } from 'ts-mockery'; +import { Settings, UiSettings } from '../../src/settings/reducers/settings'; +import { UserInterface } from '../../src/settings/UserInterface'; +import ToggleSwitch from '../../src/utils/ToggleSwitch'; +import { Theme } from '../../src/utils/theme'; + +describe('', () => { + let wrapper: ShallowWrapper; + const setUiSettings = jest.fn(); + const createWrapper = (ui?: UiSettings) => { + wrapper = shallow( + ({ ui })} + setUiSettings={setUiSettings} + />, + ); + + return wrapper; + }; + + afterEach(() => wrapper?.unmount()); + afterEach(jest.clearAllMocks); + + it.each([ + [{ theme: 'dark' as Theme }, true ], + [{ theme: 'light' as Theme }, false ], + [ undefined, false ], + ])('switch is toggled if theme is dark', (ui, expectedChecked) => { + const wrapper = createWrapper(ui); + const toggle = wrapper.find(ToggleSwitch); + + expect(toggle.prop('checked')).toEqual(expectedChecked); + }); + + it.each([ + [ true, 'dark' ], + [ false, 'light' ], + ])('invokes setUiSettings when toggle value changes', (checked, theme) => { + const wrapper = createWrapper(); + const toggle = wrapper.find(ToggleSwitch); + + expect(setUiSettings).not.toHaveBeenCalled(); + toggle.simulate('change', checked); + expect(setUiSettings).toHaveBeenCalledWith({ theme }); + }); +}); From 83791157ce782842df332511214855a7f99b6158 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 17 Feb 2021 11:03:42 +0100 Subject: [PATCH 06/18] Fixed inputs colors in dark theme when they are outside of cards --- src/index.scss | 11 ++++++++++- src/utils/DateInput.scss | 6 +++++- src/utils/DropdownBtn.scss | 13 +++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/index.scss b/src/index.scss index 691c537e..748664b3 100644 --- a/src/index.scss +++ b/src/index.scss @@ -44,6 +44,10 @@ body, background-color: var(--primary-color); } +.list-group { + background-color: var(--primary-color); +} + .modal-content, .page-link, .page-item.disabled .page-link, @@ -121,10 +125,15 @@ body, .form-control, .form-control:focus { - background-color: var(--input-color); + background-color: var(--primary-color); border-color: var(--input-border-color); } +.card .form-control, +.card .form-control:hover { + background-color: var(--input-color); +} + .navbar-brand { @media (max-width: $smMax) { margin: 0 auto !important; diff --git a/src/utils/DateInput.scss b/src/utils/DateInput.scss index ee27d849..6e3eb439 100644 --- a/src/utils/DateInput.scss +++ b/src/utils/DateInput.scss @@ -10,7 +10,11 @@ } .date-input-container__input:not(:disabled) { - background-color: #ffffff !important; + background-color: var(--primary-color) !important; +} + +.card .date-input-container__input:not(:disabled) { + background-color: var(--input-color) !important; } .date-input-container__icon { diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss index d81c18fe..cd3161bf 100644 --- a/src/utils/DropdownBtn.scss +++ b/src/utils/DropdownBtn.scss @@ -6,12 +6,21 @@ .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: var(--input-color); text-align: left; + color: #6c757d; + background-color: var(--primary-color); border-color: var(--input-border-color); } +.card .dropdown-btn__toggle.dropdown-btn__toggle, +.card .dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled).active, +.card .dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):active, +.card .dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):focus, +.card .dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):hover, +.show > .card .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle { + background-color: var(--input-color); +} + .dropdown-btn__toggle.dropdown-btn__toggle:after { @include vertical-align(); From 9703eba6ec162836da7f429251a71debdbf3ae39 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 17 Feb 2021 11:15:35 +0100 Subject: [PATCH 07/18] Fixed styles for disabled inputs in dark theme --- src/index.scss | 10 ++++++++-- src/theme/theme.scss | 4 ++++ src/utils/DropdownBtn.scss | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/index.scss b/src/index.scss index 748664b3..f4be25a1 100644 --- a/src/index.scss +++ b/src/index.scss @@ -129,8 +129,14 @@ body, border-color: var(--input-border-color); } -.card .form-control, -.card .form-control:hover { +.form-control.disabled, +.form-control:disabled { + background-color: var(--input-disabled-color); + cursor: not-allowed; +} + +.card .form-control:not(:disabled), +.card .form-control:not(:disabled):hover { background-color: var(--input-color); } diff --git a/src/theme/theme.scss b/src/theme/theme.scss index 5d9dd194..042de9c3 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -9,6 +9,7 @@ $lightBorderColor: rgba(0, 0, 0, .125); $lightActiveColor: $lightGrey; $lightBrandColor: $mainColor; $lightInputColor: $lightPrimaryColor; +$lightDisabledInputColor: $lightColor; $lightBorderInputColor: rgba(0, 0, 0, .19); // Dark theme colors @@ -20,6 +21,7 @@ $darkBorderColor: rgba(0, 0, 0, .2); $darkActiveColor: $darkSecondaryColor; $darkBrandColor: #0b2d4e; $darkInputColor: darken($darkPrimaryColor, 2%); +$darkDisabledInputColor: lighten($darkPrimaryColor, 2%); $darkBorderInputColor: rgba(0, 0, 0, .3); html:not([data-theme='dark']) { @@ -31,6 +33,7 @@ html:not([data-theme='dark']) { --active-color: #{$lightActiveColor}; --brand-color: #{$lightBrandColor}; --input-color: #{$lightInputColor}; + --input-disabled-color: #{$lightDisabledInputColor}; --input-border-color: #{$lightBorderInputColor}; } @@ -43,5 +46,6 @@ html[data-theme='dark'] { --active-color: #{$darkActiveColor}; --brand-color: #{$darkBrandColor}; --input-color: #{$darkInputColor}; + --input-disabled-color: #{$darkDisabledInputColor}; --input-border-color: #{$darkBorderInputColor}; } diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss index cd3161bf..f630328e 100644 --- a/src/utils/DropdownBtn.scss +++ b/src/utils/DropdownBtn.scss @@ -21,6 +21,11 @@ background-color: var(--input-color); } +.dropdown-btn__toggle.dropdown-btn__toggle.disabled, +.dropdown-btn__toggle.dropdown-btn__toggle:disabled { + background-color: var(--input-disabled-color); +} + .dropdown-btn__toggle.dropdown-btn__toggle:after { @include vertical-align(); From 9523277311555759fad3691ba802df89babaf916 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Feb 2021 18:55:03 +0100 Subject: [PATCH 08/18] Added icon to show which theme is selected --- src/settings/UserInterface.scss | 4 ++++ src/settings/UserInterface.tsx | 4 ++++ test/settings/UserInterface.test.tsx | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/settings/UserInterface.scss diff --git a/src/settings/UserInterface.scss b/src/settings/UserInterface.scss new file mode 100644 index 00000000..121b0784 --- /dev/null +++ b/src/settings/UserInterface.scss @@ -0,0 +1,4 @@ +.user-interface__theme-icon { + float: right; + margin-top: .25rem; +} diff --git a/src/settings/UserInterface.tsx b/src/settings/UserInterface.tsx index 608cf65f..3180ebc4 100644 --- a/src/settings/UserInterface.tsx +++ b/src/settings/UserInterface.tsx @@ -1,8 +1,11 @@ import { FC } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSun, faMoon } from '@fortawesome/free-solid-svg-icons'; import { SimpleCard } from '../utils/SimpleCard'; import ToggleSwitch from '../utils/ToggleSwitch'; import { changeThemeInMarkup, Theme } from '../utils/theme'; import { Settings, UiSettings } from './reducers/settings'; +import './UserInterface.scss'; interface UserInterfaceProps { settings: Settings; @@ -11,6 +14,7 @@ interface UserInterfaceProps { export const UserInterface: FC = ({ settings: { ui }, setUiSettings }) => ( + { diff --git a/test/settings/UserInterface.test.tsx b/test/settings/UserInterface.test.tsx index e5c295e3..02a0a037 100644 --- a/test/settings/UserInterface.test.tsx +++ b/test/settings/UserInterface.test.tsx @@ -1,5 +1,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; +import { faMoon, faSun } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Settings, UiSettings } from '../../src/settings/reducers/settings'; import { UserInterface } from '../../src/settings/UserInterface'; import ToggleSwitch from '../../src/utils/ToggleSwitch'; @@ -26,13 +28,24 @@ describe('', () => { [{ theme: 'dark' as Theme }, true ], [{ theme: 'light' as Theme }, false ], [ undefined, false ], - ])('switch is toggled if theme is dark', (ui, expectedChecked) => { + ])('toggles switch if theme is dark', (ui, expectedChecked) => { const wrapper = createWrapper(ui); const toggle = wrapper.find(ToggleSwitch); expect(toggle.prop('checked')).toEqual(expectedChecked); }); + it.each([ + [{ theme: 'dark' as Theme }, faMoon ], + [{ theme: 'light' as Theme }, faSun ], + [ undefined, faSun ], + ])('shows different icons based on theme', (ui, expectedIcon) => { + const wrapper = createWrapper(ui); + const icon = wrapper.find(FontAwesomeIcon); + + expect(icon.prop('icon')).toEqual(expectedIcon); + }); + it.each([ [ true, 'dark' ], [ false, 'light' ], From 61af43f9d9fe0478f977b242ee051761b13589af Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Feb 2021 19:30:29 +0100 Subject: [PATCH 09/18] Fixed visits table styles for dark theme --- src/index.scss | 8 +++++++- src/theme/theme.scss | 4 ++++ src/utils/DateInput.scss | 3 ++- src/utils/mixins/sticky-cell.scss | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/index.scss b/src/index.scss index f4be25a1..9bb67870 100644 --- a/src/index.scss +++ b/src/index.scss @@ -65,10 +65,16 @@ body, .page-link, .page-link:hover, .page-item.disabled .page-link, -.dropdown-divider { +.dropdown-divider{ border-color: var(--border-color); } +.table-bordered, +.table-bordered thead th, +.table-bordered thead td { + border-color: var(--table-border-color); +} + .page-link:hover { background-color: var(--secondary-color); } diff --git a/src/theme/theme.scss b/src/theme/theme.scss index 042de9c3..ba9c1e3c 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -6,6 +6,7 @@ $lightPrimaryColorAlfa: rgba($lightPrimaryColor, .5); $lightSecondaryColor: $lightColor; $lightTextColor: #212529; $lightBorderColor: rgba(0, 0, 0, .125); +$lightTableBorderColor: $mediumGrey; $lightActiveColor: $lightGrey; $lightBrandColor: $mainColor; $lightInputColor: $lightPrimaryColor; @@ -18,6 +19,7 @@ $darkPrimaryColorAlfa: rgba($darkPrimaryColor, .7); $darkSecondaryColor: #0f131a; $darkTextColor: rgb(201, 209, 217); $darkBorderColor: rgba(0, 0, 0, .2); +$darkTableBorderColor: #12161b; $darkActiveColor: $darkSecondaryColor; $darkBrandColor: #0b2d4e; $darkInputColor: darken($darkPrimaryColor, 2%); @@ -35,6 +37,7 @@ html:not([data-theme='dark']) { --input-color: #{$lightInputColor}; --input-disabled-color: #{$lightDisabledInputColor}; --input-border-color: #{$lightBorderInputColor}; + --table-border-color: #{$lightTableBorderColor}; } html[data-theme='dark'] { @@ -48,4 +51,5 @@ html[data-theme='dark'] { --input-color: #{$darkInputColor}; --input-disabled-color: #{$darkDisabledInputColor}; --input-border-color: #{$darkBorderInputColor}; + --table-border-color: #{$darkTableBorderColor}; } diff --git a/src/utils/DateInput.scss b/src/utils/DateInput.scss index 6e3eb439..c33e71de 100644 --- a/src/utils/DateInput.scss +++ b/src/utils/DateInput.scss @@ -13,7 +13,8 @@ background-color: var(--primary-color) !important; } -.card .date-input-container__input:not(:disabled) { +.card .date-input-container__input:not(:disabled), +.dropdown .date-input-container__input:not(:disabled) { background-color: var(--input-color) !important; } diff --git a/src/utils/mixins/sticky-cell.scss b/src/utils/mixins/sticky-cell.scss index a20f341f..0e2d6125 100644 --- a/src/utils/mixins/sticky-cell.scss +++ b/src/utils/mixins/sticky-cell.scss @@ -12,7 +12,7 @@ left: 0; bottom: -1px; right: -1px; - background: $mediumGrey; + background: var(--table-border-color); z-index: -2; } @@ -27,7 +27,7 @@ left: 1px; bottom: 0; right: 0; - background: white; + background: var(--primary-color); z-index: -1; } From d6633f75559c7f5110644610ded13cd46b74ebb9 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Feb 2021 20:18:02 +0100 Subject: [PATCH 10/18] More dark theme styles for visits page --- src/index.scss | 10 +++++++++- src/settings/Settings.tsx | 2 +- src/theme/theme.scss | 4 ++++ src/utils/DropdownBtn.scss | 2 ++ src/utils/theme/index.ts | 12 +++++++++++- src/visits/VisitsTable.tsx | 2 +- src/visits/helpers/DefaultChart.tsx | 12 ++++++++++-- test/visits/VisitsTable.test.tsx | 2 +- 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/index.scss b/src/index.scss index 9bb67870..ea594f29 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,3 +1,5 @@ +/* stylelint-disable no-descending-specificity */ + @import './utils/base'; @import 'node_modules/bootstrap/scss/bootstrap.scss'; @import './common/react-tagsinput.scss'; @@ -65,7 +67,7 @@ body, .page-link, .page-link:hover, .page-item.disabled .page-link, -.dropdown-divider{ +.dropdown-divider { border-color: var(--border-color); } @@ -146,6 +148,12 @@ body, background-color: var(--input-color); } +.table-active, +.table-active > th, +.table-active > td { + background-color: var(--table-highlight-color) !important; +} + .navbar-brand { @media (max-width: $smMax) { margin: 0 auto !important; diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 31e6fc6f..8e6731f5 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -2,7 +2,7 @@ import { FC } from 'react'; import { Row } from 'reactstrap'; import NoMenuLayout from '../common/NoMenuLayout'; -const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC, UserInterface:FC) => () => ( +const Settings = (RealTimeUpdates: FC, ShortUrlCreation: FC, UserInterface: FC) => () => (
diff --git a/src/theme/theme.scss b/src/theme/theme.scss index ba9c1e3c..3b7b8d57 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -12,6 +12,7 @@ $lightBrandColor: $mainColor; $lightInputColor: $lightPrimaryColor; $lightDisabledInputColor: $lightColor; $lightBorderInputColor: rgba(0, 0, 0, .19); +$lightTableHighlightColor: rgba(0, 0, 0, .075); // Dark theme colors $darkPrimaryColor: #161b22; @@ -25,6 +26,7 @@ $darkBrandColor: #0b2d4e; $darkInputColor: darken($darkPrimaryColor, 2%); $darkDisabledInputColor: lighten($darkPrimaryColor, 2%); $darkBorderInputColor: rgba(0, 0, 0, .3); +$darkTableHighlightColor: $darkBorderColor; html:not([data-theme='dark']) { --primary-color: #{$lightPrimaryColor}; @@ -38,6 +40,7 @@ html:not([data-theme='dark']) { --input-disabled-color: #{$lightDisabledInputColor}; --input-border-color: #{$lightBorderInputColor}; --table-border-color: #{$lightTableBorderColor}; + --table-highlight-color: #{$lightTableHighlightColor}; } html[data-theme='dark'] { @@ -52,4 +55,5 @@ html[data-theme='dark'] { --input-disabled-color: #{$darkDisabledInputColor}; --input-border-color: #{$darkBorderInputColor}; --table-border-color: #{$darkTableBorderColor}; + --table-highlight-color: #{$darkTableHighlightColor}; } diff --git a/src/utils/DropdownBtn.scss b/src/utils/DropdownBtn.scss index f630328e..663e8e09 100644 --- a/src/utils/DropdownBtn.scss +++ b/src/utils/DropdownBtn.scss @@ -1,3 +1,5 @@ +/* stylelint-disable no-descending-specificity */ + @import '../utils/mixins/vertical-align'; .dropdown-btn__toggle.dropdown-btn__toggle, diff --git a/src/utils/theme/index.ts b/src/utils/theme/index.ts index ca56555b..3f47f7eb 100644 --- a/src/utils/theme/index.ts +++ b/src/utils/theme/index.ts @@ -2,10 +2,14 @@ export const MAIN_COLOR = '#4696e5'; export const MAIN_COLOR_ALPHA = 'rgba(70, 150, 229, 0.4)'; -export const HIGHLIGHTED_COLOR = '#F77F28'; +export const HIGHLIGHTED_COLOR = '#f77f28'; export const HIGHLIGHTED_COLOR_ALPHA = 'rgba(247, 127, 40, 0.4)'; +export const PRIMARY_LIGHT_COLOR = 'white'; + +export const PRIMARY_DARK_COLOR = '#161b22'; + export type Theme = 'dark' | 'light'; export const changeThemeInMarkup = (theme: Theme) => { @@ -13,3 +17,9 @@ export const changeThemeInMarkup = (theme: Theme) => { html?.[0]?.setAttribute('data-theme', theme); }; + +export const isDarkThemeEnabled = (): boolean => { + const html = document.getElementsByTagName('html'); + + return html?.[0]?.getAttribute('data-theme') === 'dark'; +}; diff --git a/src/visits/VisitsTable.tsx b/src/visits/VisitsTable.tsx index 54e16602..fb99a950 100644 --- a/src/visits/VisitsTable.tsx +++ b/src/visits/VisitsTable.tsx @@ -154,7 +154,7 @@ const VisitsTable = ({ setSelectedVisits( isSelected ? selectedVisits.filter((v) => v !== visit) : [ ...selectedVisits, visit ], )} diff --git a/src/visits/helpers/DefaultChart.tsx b/src/visits/helpers/DefaultChart.tsx index 78d1863a..74dd083a 100644 --- a/src/visits/helpers/DefaultChart.tsx +++ b/src/visits/helpers/DefaultChart.tsx @@ -7,7 +7,15 @@ import { fillTheGaps } from '../../utils/helpers/visits'; import { Stats } from '../types'; import { prettify } from '../../utils/helpers/numbers'; import { pointerOnHover, renderDoughnutChartLabel, renderNonDoughnutChartLabel } from '../../utils/helpers/charts'; -import { HIGHLIGHTED_COLOR, HIGHLIGHTED_COLOR_ALPHA, MAIN_COLOR, MAIN_COLOR_ALPHA } from '../../utils/theme'; +import { + HIGHLIGHTED_COLOR, + HIGHLIGHTED_COLOR_ALPHA, + isDarkThemeEnabled, + MAIN_COLOR, + MAIN_COLOR_ALPHA, + PRIMARY_DARK_COLOR, + PRIMARY_LIGHT_COLOR, +} from '../../utils/theme'; import './DefaultChart.scss'; export interface DefaultChartProps { @@ -47,7 +55,7 @@ const generateGraphData = ( '#DCDCDC', '#463730', ], - borderColor: isBarChart ? MAIN_COLOR : 'white', + borderColor: isBarChart ? MAIN_COLOR : (isDarkThemeEnabled() ? PRIMARY_DARK_COLOR : PRIMARY_LIGHT_COLOR), borderWidth: 2, }, highlightedData && { diff --git a/test/visits/VisitsTable.test.tsx b/test/visits/VisitsTable.test.tsx index 339233d0..a2d01c4a 100644 --- a/test/visits/VisitsTable.test.tsx +++ b/test/visits/VisitsTable.test.tsx @@ -85,7 +85,7 @@ describe('', () => { ); expect(wrapper.find('.text-primary')).toHaveLength(3); - expect(wrapper.find('.table-primary')).toHaveLength(2); + expect(wrapper.find('.table-active')).toHaveLength(2); // Select one extra wrapper.find('tr').at(5).simulate('click'); From 36c97ad804c62787036e2a03e150462c8c464db7 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Feb 2021 20:51:55 +0100 Subject: [PATCH 11/18] Updated styles in tags section to make it more dark-theme friendly --- src/tags/TagCard.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tags/TagCard.tsx b/src/tags/TagCard.tsx index d7359b25..b72bca0e 100644 --- a/src/tags/TagCard.tsx +++ b/src/tags/TagCard.tsx @@ -35,10 +35,10 @@ const TagCard = ( return ( - -
@@ -57,14 +57,14 @@ const TagCard = ( Short URLs {prettify(tagStats.shortUrlsCount)} Visits {prettify(tagStats.visitsCount)} From 0ee899f309ecc470250d6ba98f386cc0b5ef5491 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 19 Feb 2021 21:01:24 +0100 Subject: [PATCH 12/18] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9880d0d..a98b261e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), * [#379](https://github.com/shlinkio/shlink-web-client/issues/379) and [#384](https://github.com/shlinkio/shlink-web-client/issues/384) Improved QR code modal, including controls to customize size, format and margin, as well as a button to copy the link to the clipboard. * [#385](https://github.com/shlinkio/shlink-web-client/issues/385) Added setting to determine if "validate URL" should be enabled or disabled by default. * [#386](https://github.com/shlinkio/shlink-web-client/issues/386) Added new card in overview section to display amount of orphan visits when using Shlink 2.6.0 or higher. +* [#177](https://github.com/shlinkio/shlink-web-client/issues/177) Added dark theme. **[BETA]** ### Changed * *Nothing* From e7466ced18884e08e7c2ee706229e91203635031 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 20 Feb 2021 09:02:07 +0100 Subject: [PATCH 13/18] Added dark theme styles for date picker --- src/index.scss | 3 ++- src/utils/DateInput.scss | 54 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/index.scss b/src/index.scss index ea594f29..407826cd 100644 --- a/src/index.scss +++ b/src/index.scss @@ -101,7 +101,8 @@ body, } } -.dropdown-item { +.dropdown-item, +.dropdown-item-text { color: var(--text-color); } diff --git a/src/utils/DateInput.scss b/src/utils/DateInput.scss index c33e71de..50c4016a 100644 --- a/src/utils/DateInput.scss +++ b/src/utils/DateInput.scss @@ -43,10 +43,6 @@ display: block !important; } -.react-datepicker-popper { - z-index: 2; -} - .react-datepicker__day--keyboard-selected { background-color: $mainColor; @@ -54,3 +50,53 @@ background-color: darken($mainColor, 12%); } } + +.react-datepicker.react-datepicker { + background-color: var(--primary-color); + color: var(--text-color); + border-color: var(--border-color); +} + +.react-datepicker__header.react-datepicker__header { + background-color: var(--secondary-color); + border-color: var(--border-color); +} + +.react-datepicker__current-month.react-datepicker__current-month, +.react-datepicker-time__header.react-datepicker-time__header, +.react-datepicker-year-header.react-datepicker-year-header, +.react-datepicker__day-name.react-datepicker__day-name, +.react-datepicker__day:not(:hover).react-datepicker__day:not(:hover), +.react-datepicker__time-name.react-datepicker__time-name { + color: inherit; +} + +.react-datepicker__day--keyboard-selected.react-datepicker__day--keyboard-selected, +.react-datepicker__month-text--keyboard-selected.react-datepicker__month-text--keyboard-selected, +.react-datepicker__quarter-text--keyboard-selected.react-datepicker__quarter-text--keyboard-selected, +.react-datepicker__year-text--keyboard-selected.react-datepicker__year-text--keyboard-selected { + background-color: var(--brand-color) !important; + color: white !important; +} + +.react-datepicker-popper.react-datepicker-popper { + z-index: 2; + + &[data-placement^='top'] .react-datepicker__triangle.react-datepicker__triangle { + border-top-color: var(--primary-color); + border-bottom-color: var(--border-color); + + &::before { + border-top-color: var(--border-color); + } + } + + &[data-placement^='bottom'] .react-datepicker__triangle.react-datepicker__triangle { + border-top-color: var(--border-color); + border-bottom-color: var(--secondary-color); + + &::before { + border-bottom-color: var(--border-color); + } + } +} From f97ef8df83291f2eb975057466bf83810836f889 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 20 Feb 2021 13:22:45 +0100 Subject: [PATCH 14/18] Added proper blurred background for modals --- src/index.scss | 10 ---------- src/servers/DeleteServerModal.tsx | 7 ++++--- src/short-urls/UseExistingIfFoundInfoIcon.tsx | 9 +++++---- src/short-urls/helpers/DeleteShortUrlModal.tsx | 7 ++++--- src/short-urls/helpers/EditMetaModal.tsx | 7 ++++--- src/short-urls/helpers/EditShortUrlModal.tsx | 7 ++++--- src/short-urls/helpers/EditTagsModal.tsx | 7 ++++--- src/short-urls/helpers/QrCodeModal.tsx | 7 ++++--- src/tags/helpers/DeleteTagConfirmModal.tsx | 7 ++++--- src/tags/helpers/EditTagModal.tsx | 7 ++++--- src/utils/BlurredModal.scss | 11 +++++++++++ src/utils/BlurredModal.tsx | 12 ++++++++++++ src/visits/helpers/MapModal.tsx | 7 ++++--- test/servers/DeleteServerModal.test.tsx | 5 +++-- test/short-urls/helpers/EditMetaModal.test.tsx | 2 +- test/short-urls/helpers/EditShortUrlModal.test.tsx | 2 +- test/short-urls/helpers/EditTagsModal.test.tsx | 4 ++-- test/short-urls/helpers/QrCodeModal.test.tsx | 5 +++-- test/tags/helpers/DeleteTagConfirmModal.test.tsx | 5 +++-- test/visits/helpers/MapModal.test.tsx | 4 ++-- 20 files changed, 79 insertions(+), 53 deletions(-) create mode 100644 src/utils/BlurredModal.scss create mode 100644 src/utils/BlurredModal.tsx diff --git a/src/index.scss b/src/index.scss index 407826cd..8926936a 100644 --- a/src/index.scss +++ b/src/index.scss @@ -17,16 +17,6 @@ body, color: var(--text-color); } -@media (min-width: $smMin) { - #root { - transition: filter 300ms; - } - - .modal-open #root { - filter: blur(1.5px); - } -} - .bg-main { background-color: $mainColor !important; } diff --git a/src/servers/DeleteServerModal.tsx b/src/servers/DeleteServerModal.tsx index 4d74869e..2c2c2eee 100644 --- a/src/servers/DeleteServerModal.tsx +++ b/src/servers/DeleteServerModal.tsx @@ -1,5 +1,6 @@ -import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { RouterProps } from 'react-router'; +import { BlurredModal } from '../utils/BlurredModal'; import { ServerWithId } from './data'; export interface DeleteServerModalProps { @@ -20,7 +21,7 @@ const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }: De }; return ( - + Remove server

Are you sure you want to remove {server ? server.name : ''}?

@@ -35,7 +36,7 @@ const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }: De -
+ ); }; diff --git a/src/short-urls/UseExistingIfFoundInfoIcon.tsx b/src/short-urls/UseExistingIfFoundInfoIcon.tsx index c737d690..3db7c9b1 100644 --- a/src/short-urls/UseExistingIfFoundInfoIcon.tsx +++ b/src/short-urls/UseExistingIfFoundInfoIcon.tsx @@ -1,11 +1,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; -import { Modal, ModalBody, ModalHeader } from 'reactstrap'; -import './UseExistingIfFoundInfoIcon.scss'; +import { ModalBody, ModalHeader } from 'reactstrap'; import { useToggle } from '../utils/helpers/hooks'; +import { BlurredModal } from '../utils/BlurredModal'; +import './UseExistingIfFoundInfoIcon.scss'; const InfoModal = ({ isOpen, toggle }: { isOpen: boolean; toggle: () => void }) => ( - + Info

@@ -33,7 +34,7 @@ const InfoModal = ({ isOpen, toggle }: { isOpen: boolean; toggle: () => void }) - + ); const UseExistingIfFoundInfoIcon = () => { diff --git a/src/short-urls/helpers/DeleteShortUrlModal.tsx b/src/short-urls/helpers/DeleteShortUrlModal.tsx index 435b3318..621a03fb 100644 --- a/src/short-urls/helpers/DeleteShortUrlModal.tsx +++ b/src/short-urls/helpers/DeleteShortUrlModal.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { identity, pipe } from 'ramda'; import { ShortUrlDeletion } from '../reducers/shortUrlDeletion'; import { ShortUrlModalProps } from '../data'; @@ -7,6 +7,7 @@ import { handleEventPreventingDefault, OptionalString } from '../../utils/utils' import { Result } from '../../utils/Result'; import { isInvalidDeletionError } from '../../api/utils'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; interface DeleteShortUrlModalConnectProps extends ShortUrlModalProps { shortUrlDeletion: ShortUrlDeletion; @@ -32,7 +33,7 @@ const DeleteShortUrlModal = ( }); return ( - +

Delete short URL @@ -67,7 +68,7 @@ const DeleteShortUrlModal = ( -
+ ); }; diff --git a/src/short-urls/helpers/EditMetaModal.tsx b/src/short-urls/helpers/EditMetaModal.tsx index 688cf9be..8b8d1b4c 100644 --- a/src/short-urls/helpers/EditMetaModal.tsx +++ b/src/short-urls/helpers/EditMetaModal.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, useState } from 'react'; -import { Modal, ModalBody, ModalFooter, ModalHeader, FormGroup, Input, UncontrolledTooltip } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader, FormGroup, Input, UncontrolledTooltip } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; import { ExternalLink } from 'react-external-link'; @@ -12,6 +12,7 @@ import { ShortUrl, ShortUrlMeta, ShortUrlModalProps } from '../data'; import { handleEventPreventingDefault, Nullable, OptionalString } from '../../utils/utils'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; interface EditMetaModalConnectProps extends ShortUrlModalProps { shortUrlMeta: ShortUrlMetaEdition; @@ -42,7 +43,7 @@ const EditMetaModal = ( }).then(close); return ( - + Edit metadata for @@ -94,7 +95,7 @@ const EditMetaModal = ( - + ); }; diff --git a/src/short-urls/helpers/EditShortUrlModal.tsx b/src/short-urls/helpers/EditShortUrlModal.tsx index 0a09e27e..cee662d4 100644 --- a/src/short-urls/helpers/EditShortUrlModal.tsx +++ b/src/short-urls/helpers/EditShortUrlModal.tsx @@ -1,11 +1,12 @@ import { useState } from 'react'; -import { Modal, ModalBody, ModalFooter, ModalHeader, FormGroup, Input, Button } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader, FormGroup, Input, Button } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import { ShortUrlEdition } from '../reducers/shortUrlEdition'; import { handleEventPreventingDefault, hasValue, OptionalString } from '../../utils/utils'; import { ShortUrlModalProps } from '../data'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; interface EditShortUrlModalProps extends ShortUrlModalProps { shortUrlEdition: ShortUrlEdition; @@ -20,7 +21,7 @@ const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShor const doEdit = async () => editShortUrl(shortUrl.shortCode, shortUrl.domain, longUrl).then(toggle); return ( - + Edit long URL for @@ -49,7 +50,7 @@ const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShor - + ); }; diff --git a/src/short-urls/helpers/EditTagsModal.tsx b/src/short-urls/helpers/EditTagsModal.tsx index a390499c..bc6875ed 100644 --- a/src/short-urls/helpers/EditTagsModal.tsx +++ b/src/short-urls/helpers/EditTagsModal.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import { ShortUrlTags } from '../reducers/shortUrlTags'; import { ShortUrlModalProps } from '../data'; @@ -7,6 +7,7 @@ import { OptionalString } from '../../utils/utils'; import { TagsSelectorProps } from '../../tags/helpers/TagsSelector'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; interface EditTagsModalProps extends ShortUrlModalProps { shortUrlTags: ShortUrlTags; @@ -28,7 +29,7 @@ const EditTagsModal = (TagsSelector: FC) => ( .catch(() => {}); return ( - + Edit tags for @@ -46,7 +47,7 @@ const EditTagsModal = (TagsSelector: FC) => ( {saving ? 'Saving tags...' : 'Save tags'} - + ); }; diff --git a/src/short-urls/helpers/QrCodeModal.tsx b/src/short-urls/helpers/QrCodeModal.tsx index e26d289a..9b1c7bad 100644 --- a/src/short-urls/helpers/QrCodeModal.tsx +++ b/src/short-urls/helpers/QrCodeModal.tsx @@ -1,5 +1,5 @@ import { useMemo, useState } from 'react'; -import { DropdownItem, FormGroup, Modal, ModalBody, ModalHeader, Row } from 'reactstrap'; +import { DropdownItem, FormGroup, ModalBody, ModalHeader, Row } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import classNames from 'classnames'; import { ShortUrlModalProps } from '../data'; @@ -9,6 +9,7 @@ import { DropdownBtn } from '../../utils/DropdownBtn'; import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon'; import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat } from '../../utils/helpers/qrCodes'; import './QrCodeModal.scss'; +import { BlurredModal } from '../../utils/BlurredModal'; interface QrCodeModalConnectProps extends ShortUrlModalProps { selectedServer: ReachableServer; @@ -37,7 +38,7 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }: }, [ totalSize ]); return ( - + QR code for {shortUrl} @@ -98,7 +99,7 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }:
{size}x{size}
- + ); }; diff --git a/src/tags/helpers/DeleteTagConfirmModal.tsx b/src/tags/helpers/DeleteTagConfirmModal.tsx index c4ef46ab..dc4d2838 100644 --- a/src/tags/helpers/DeleteTagConfirmModal.tsx +++ b/src/tags/helpers/DeleteTagConfirmModal.tsx @@ -1,8 +1,9 @@ -import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { TagDeletion } from '../reducers/tagDelete'; import { TagModalProps } from '../data'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; interface DeleteTagConfirmModalProps extends TagModalProps { deleteTag: (tag: string) => Promise; @@ -21,7 +22,7 @@ const DeleteTagConfirmModal = ( }; return ( - + Delete tag @@ -39,7 +40,7 @@ const DeleteTagConfirmModal = ( {deleting ? 'Deleting tag...' : 'Delete tag'} - + ); }; diff --git a/src/tags/helpers/EditTagModal.tsx b/src/tags/helpers/EditTagModal.tsx index 7cbd5169..938d8b6d 100644 --- a/src/tags/helpers/EditTagModal.tsx +++ b/src/tags/helpers/EditTagModal.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { Modal, ModalBody, ModalFooter, ModalHeader, Popover } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader, Popover } from 'reactstrap'; import { ChromePicker } from 'react-color'; import { faPalette as colorIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -10,6 +10,7 @@ import { TagModalProps } from '../data'; import { TagEdition } from '../reducers/tagEdit'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; +import { BlurredModal } from '../../utils/BlurredModal'; import './EditTagModal.scss'; interface EditTagModalProps extends TagModalProps { @@ -31,7 +32,7 @@ const EditTagModal = ({ getColorForKey }: ColorGenerator) => ( .catch(() => {})); return ( - +
Edit tag @@ -68,7 +69,7 @@ const EditTagModal = ({ getColorForKey }: ColorGenerator) => ( -
+ ); }; diff --git a/src/utils/BlurredModal.scss b/src/utils/BlurredModal.scss new file mode 100644 index 00000000..66d4e435 --- /dev/null +++ b/src/utils/BlurredModal.scss @@ -0,0 +1,11 @@ +@import './base'; + +@media (min-width: $smMin) { + #root { + transition: filter 300ms; + } + + .with-modal #root { + filter: blur(1.5px); + } +} diff --git a/src/utils/BlurredModal.tsx b/src/utils/BlurredModal.tsx new file mode 100644 index 00000000..62f68373 --- /dev/null +++ b/src/utils/BlurredModal.tsx @@ -0,0 +1,12 @@ +import { FC } from 'react'; +import { ModalProps, Modal } from 'reactstrap'; // eslint-disable-line import/named +import './BlurredModal.scss'; + +const onEnter = () => document.body.classList.add('with-modal'); +const onExit = () => document.body.classList.remove('with-modal'); + +export const BlurredModal: FC = ({ children, ...rest }) => ( + + {children} + +); diff --git a/src/visits/helpers/MapModal.tsx b/src/visits/helpers/MapModal.tsx index ae57ed12..4d29be2a 100644 --- a/src/visits/helpers/MapModal.tsx +++ b/src/visits/helpers/MapModal.tsx @@ -1,8 +1,9 @@ import { FC } from 'react'; -import { Modal, ModalBody } from 'reactstrap'; +import { ModalBody } from 'reactstrap'; import { MapContainer, TileLayer, Marker, Popup, MapContainerProps } from 'react-leaflet'; import { prop } from 'ramda'; import { CityStats } from '../types'; +import { BlurredModal } from '../../utils/BlurredModal'; import './MapModal.scss'; interface MapModalProps { @@ -36,7 +37,7 @@ const calculateMapProps = (locations: CityStats[]): MapContainerProps => { }; const MapModal = ({ toggle, isOpen, title, locations = [] }: MapModalProps) => ( - +

{title} @@ -51,7 +52,7 @@ const MapModal = ({ toggle, isOpen, title, locations = [] }: MapModalProps) => ( ))} - + ); export default MapModal; diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index 14eed177..fc844b32 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -1,9 +1,10 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { History } from 'history'; import { Mock } from 'ts-mockery'; import DeleteServerModal from '../../src/servers/DeleteServerModal'; import { ServerWithId } from '../../src/servers/data'; +import { BlurredModal } from '../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -27,7 +28,7 @@ describe('', () => { afterEach(jest.clearAllMocks); it('renders a modal window', () => { - expect(wrapper.find(Modal)).toHaveLength(1); + expect(wrapper.find(BlurredModal)).toHaveLength(1); expect(wrapper.find(ModalHeader)).toHaveLength(1); expect(wrapper.find(ModalBody)).toHaveLength(1); expect(wrapper.find(ModalFooter)).toHaveLength(1); diff --git a/test/short-urls/helpers/EditMetaModal.test.tsx b/test/short-urls/helpers/EditMetaModal.test.tsx index b5553125..b06f2a40 100644 --- a/test/short-urls/helpers/EditMetaModal.test.tsx +++ b/test/short-urls/helpers/EditMetaModal.test.tsx @@ -71,7 +71,7 @@ describe('', () => { it.each([ [ '.btn-link', 'onClick' ], - [ 'Modal', 'toggle' ], + [ 'BlurredModal', 'toggle' ], [ 'ModalHeader', 'toggle' ], ])('resets meta when modal is toggled in any way', (componentToFind, propToCall) => { const wrapper = createWrapper({ saving: false, error: false }); diff --git a/test/short-urls/helpers/EditShortUrlModal.test.tsx b/test/short-urls/helpers/EditShortUrlModal.test.tsx index 2ab14c4b..0400d379 100644 --- a/test/short-urls/helpers/EditShortUrlModal.test.tsx +++ b/test/short-urls/helpers/EditShortUrlModal.test.tsx @@ -67,7 +67,7 @@ describe('', () => { it.each([ [ '[color="link"]', 'onClick' ], - [ 'Modal', 'toggle' ], + [ 'BlurredModal', 'toggle' ], [ 'ModalHeader', 'toggle' ], ])('toggles modal with different mechanisms', (componentToFind, propToCall) => { const wrapper = createWrapper({}, { saving: false, error: false }); diff --git a/test/short-urls/helpers/EditTagsModal.test.tsx b/test/short-urls/helpers/EditTagsModal.test.tsx index 3da11f62..bbff8bb7 100644 --- a/test/short-urls/helpers/EditTagsModal.test.tsx +++ b/test/short-urls/helpers/EditTagsModal.test.tsx @@ -1,10 +1,10 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Modal } from 'reactstrap'; import { Mock } from 'ts-mockery'; import createEditTagsModal from '../../../src/short-urls/helpers/EditTagsModal'; import { ShortUrl } from '../../../src/short-urls/data'; import { ShortUrlTags } from '../../../src/short-urls/reducers/shortUrlTags'; import { OptionalString } from '../../../src/utils/utils'; +import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -98,7 +98,7 @@ describe('', () => { saving: false, error: false, }); - const modal = wrapper.find(Modal); + const modal = wrapper.find(BlurredModal); modal.simulate('closed'); expect(editShortUrlTags).not.toHaveBeenCalled(); diff --git a/test/short-urls/helpers/QrCodeModal.test.tsx b/test/short-urls/helpers/QrCodeModal.test.tsx index d84b5572..67089d06 100644 --- a/test/short-urls/helpers/QrCodeModal.test.tsx +++ b/test/short-urls/helpers/QrCodeModal.test.tsx @@ -1,12 +1,13 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { ExternalLink } from 'react-external-link'; -import { Modal, ModalBody, ModalHeader, Row } from 'reactstrap'; +import { ModalBody, ModalHeader, Row } from 'reactstrap'; import { Mock } from 'ts-mockery'; import QrCodeModal from '../../../src/short-urls/helpers/QrCodeModal'; import { ShortUrl } from '../../../src/short-urls/data'; import { ReachableServer } from '../../../src/servers/data'; import { CopyToClipboardIcon } from '../../../src/utils/CopyToClipboardIcon'; import { DropdownBtn } from '../../../src/utils/DropdownBtn'; +import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -80,7 +81,7 @@ describe('', () => { expect(wrapper.find('.mt-2').text()).toEqual(`${size}x${size}`); expect(wrapper.find('label').at(0).text()).toEqual(`Size: ${size}px`); expect(wrapper.find('label').at(1).text()).toEqual(`Margin: ${margin}px`); - expect(wrapper.find(Modal).prop('size')).toEqual(modalSize); + expect(wrapper.find(BlurredModal).prop('size')).toEqual(modalSize); }); it.each([ diff --git a/test/tags/helpers/DeleteTagConfirmModal.test.tsx b/test/tags/helpers/DeleteTagConfirmModal.test.tsx index 866dadce..090e31e4 100644 --- a/test/tags/helpers/DeleteTagConfirmModal.test.tsx +++ b/test/tags/helpers/DeleteTagConfirmModal.test.tsx @@ -1,7 +1,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Modal, ModalBody, ModalFooter } from 'reactstrap'; +import { ModalBody, ModalFooter } from 'reactstrap'; import DeleteTagConfirmModal from '../../../src/tags/helpers/DeleteTagConfirmModal'; import { TagDeletion } from '../../../src/tags/reducers/tagDelete'; +import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -68,7 +69,7 @@ describe('', () => { it('does no further actions when modal is closed without deleting tag', () => { wrapper = createWrapper({ error: false, deleting: false }); - const modal = wrapper.find(Modal); + const modal = wrapper.find(BlurredModal); modal.simulate('closed'); expect(deleteTag).not.toHaveBeenCalled(); diff --git a/test/visits/helpers/MapModal.test.tsx b/test/visits/helpers/MapModal.test.tsx index 9c286b11..da6c18ec 100644 --- a/test/visits/helpers/MapModal.test.tsx +++ b/test/visits/helpers/MapModal.test.tsx @@ -1,8 +1,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { Modal } from 'reactstrap'; import { Marker, Popup } from 'react-leaflet'; import MapModal from '../../../src/visits/helpers/MapModal'; import { CityStats } from '../../../src/visits/types'; +import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -33,7 +33,7 @@ describe('', () => { afterEach(() => wrapper.unmount()); it('renders modal with provided props', () => { - const modal = wrapper.find(Modal); + const modal = wrapper.find(BlurredModal); const header = wrapper.find('.map-modal__modal-title'); expect(modal.prop('toggle')).toEqual(toggle); From c89bcab770561949cced47b505e51edde7dcf716 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 26 Feb 2021 23:03:14 +0100 Subject: [PATCH 15/18] Improved contrast in border colors for dark theme --- src/common/Home.scss | 2 +- src/index.scss | 5 ++++- src/servers/ServersListGroup.scss | 4 ++-- src/theme/theme.scss | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/common/Home.scss b/src/common/Home.scss index 775de88f..c8a00d35 100644 --- a/src/common/Home.scss +++ b/src/common/Home.scss @@ -36,6 +36,6 @@ .home__servers-container { @media (min-width: $mdMin) { - border-left: 1px solid rgba(0, 0, 0, .125); + border-left: 1px solid var(--border-color); } } diff --git a/src/index.scss b/src/index.scss index 8926936a..cbc87b15 100644 --- a/src/index.scss +++ b/src/index.scss @@ -34,6 +34,7 @@ body, .card { box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075); background-color: var(--primary-color); + border-color: var(--border-color); } .list-group { @@ -57,7 +58,9 @@ body, .page-link, .page-link:hover, .page-item.disabled .page-link, -.dropdown-divider { +.dropdown-divider, +.dropdown-menu, +.list-group-item { border-color: var(--border-color); } diff --git a/src/servers/ServersListGroup.scss b/src/servers/ServersListGroup.scss index e51a58b6..9a0709f0 100644 --- a/src/servers/ServersListGroup.scss +++ b/src/servers/ServersListGroup.scss @@ -29,7 +29,7 @@ .servers-list__list-group--embedded.servers-list__list-group--embedded { border-radius: 0; - border-top: 1px solid rgba(0, 0, 0, .125); + border-top: 1px solid var(--border-color); @media (min-width: $mdMin) { max-height: 220px; @@ -40,6 +40,6 @@ .servers-list__server-item { border: none; - border-bottom: 1px solid rgba(0, 0, 0, .125); + border-bottom: 1px solid var(--border-color); } } diff --git a/src/theme/theme.scss b/src/theme/theme.scss index 3b7b8d57..aadcaf27 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -19,8 +19,8 @@ $darkPrimaryColor: #161b22; $darkPrimaryColorAlfa: rgba($darkPrimaryColor, .7); $darkSecondaryColor: #0f131a; $darkTextColor: rgb(201, 209, 217); -$darkBorderColor: rgba(0, 0, 0, .2); -$darkTableBorderColor: #12161b; +$darkBorderColor: rgba(255, 255, 255, .15); +$darkTableBorderColor: #393d43; $darkActiveColor: $darkSecondaryColor; $darkBrandColor: #0b2d4e; $darkInputColor: darken($darkPrimaryColor, 2%); From ae4921b86506376db3b4bc788760bdcdd941e89e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 26 Feb 2021 23:10:19 +0100 Subject: [PATCH 16/18] Improved contrast in input border colors for dark theme --- src/theme/theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme/theme.scss b/src/theme/theme.scss index aadcaf27..3316a644 100644 --- a/src/theme/theme.scss +++ b/src/theme/theme.scss @@ -25,7 +25,7 @@ $darkActiveColor: $darkSecondaryColor; $darkBrandColor: #0b2d4e; $darkInputColor: darken($darkPrimaryColor, 2%); $darkDisabledInputColor: lighten($darkPrimaryColor, 2%); -$darkBorderInputColor: rgba(0, 0, 0, .3); +$darkBorderInputColor: $darkBorderColor; $darkTableHighlightColor: $darkBorderColor; html:not([data-theme='dark']) { From 3cd30b61e488b8865ccb1f49f684d76d1c451d1c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Feb 2021 08:23:06 +0100 Subject: [PATCH 17/18] More style fixes for dark theme --- src/common/MenuLayout.scss | 2 +- src/common/react-tagsinput.scss | 8 +++++++- src/domains/DomainSelector.scss | 11 +++++++++-- src/domains/DomainSelector.tsx | 2 +- src/index.scss | 6 +++++- src/servers/Overview.scss | 2 +- src/short-urls/helpers/ShortUrlsRow.scss | 2 +- src/theme/theme.scss | 6 +++++- src/utils/DropdownBtn.scss | 2 +- src/utils/base.scss | 1 + 10 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/common/MenuLayout.scss b/src/common/MenuLayout.scss index 63f476c5..7eae132d 100644 --- a/src/common/MenuLayout.scss +++ b/src/common/MenuLayout.scss @@ -22,7 +22,7 @@ z-index: 1035; font-size: 1.5rem; cursor: pointer; - color: var(--primary-color-alfa); + color: rgba(255, 255, 255, .5); @media (max-width: $smMax) { display: inline-block; diff --git a/src/common/react-tagsinput.scss b/src/common/react-tagsinput.scss index 54a5facb..6ecd1cd3 100644 --- a/src/common/react-tagsinput.scss +++ b/src/common/react-tagsinput.scss @@ -1,3 +1,5 @@ +@import '../utils/base'; + .react-tagsinput { background-color: var(--input-color); border: 1px solid var(--input-border-color); @@ -44,7 +46,11 @@ width: 100%; margin-bottom: 6px; font-size: 1.25rem; - color: #495057; + color: var(--input-text-color); +} + +.react-tagsinput-input::placeholder { + color: $textPlaceholder; } .react-autosuggest__suggestion--highlighted { diff --git a/src/domains/DomainSelector.scss b/src/domains/DomainSelector.scss index 89e02433..729f58ea 100644 --- a/src/domains/DomainSelector.scss +++ b/src/domains/DomainSelector.scss @@ -1,12 +1,19 @@ +@import '../utils/base'; @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 { + color: $textPlaceholder !important; +} + .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 !important; + color: var(--input-text-color) !important; } .domains-dropdown__back-btn.domains-dropdown__back-btn, .domains-dropdown__back-btn.domains-dropdown__back-btn:hover { - border-color: #ced4da; + border-color: var(--border-color); } diff --git a/src/domains/DomainSelector.tsx b/src/domains/DomainSelector.tsx index c843f409..5ab1e591 100644 --- a/src/domains/DomainSelector.tsx +++ b/src/domains/DomainSelector.tsx @@ -54,7 +54,7 @@ export const DomainSelector = ({ listDomains, value, domainsList, onChange }: Do ) : ( {domains.map(({ domain, isDefault }) => ( .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle { text-align: left; - color: #6c757d; + color: var(--input-text-color); background-color: var(--primary-color); border-color: var(--input-border-color); } diff --git a/src/utils/base.scss b/src/utils/base.scss index ff82f97f..d5c52b2c 100644 --- a/src/utils/base.scss +++ b/src/utils/base.scss @@ -15,6 +15,7 @@ $lightColor: #f5f6fe; $lightGrey: #eeeeee; $dangerColor: #dc3545; $mediumGrey: #dee2e6; +$textPlaceholder: #6c757d; // Misc $headerHeight: 57px; From 2553b27d7df7f0f9f4c3aaf079e0e836373a6676 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 27 Feb 2021 08:52:10 +0100 Subject: [PATCH 18/18] Rolled-back blurred modal --- CHANGELOG.md | 2 +- src/servers/DeleteServerModal.tsx | 7 +++---- src/short-urls/UseExistingIfFoundInfoIcon.tsx | 7 +++---- src/short-urls/helpers/DeleteShortUrlModal.tsx | 7 +++---- src/short-urls/helpers/EditMetaModal.tsx | 7 +++---- src/short-urls/helpers/EditShortUrlModal.tsx | 7 +++---- src/short-urls/helpers/EditTagsModal.tsx | 7 +++---- src/short-urls/helpers/QrCodeModal.tsx | 7 +++---- src/tags/helpers/DeleteTagConfirmModal.tsx | 7 +++---- src/tags/helpers/EditTagModal.tsx | 7 +++---- src/utils/BlurredModal.scss | 11 ----------- src/utils/BlurredModal.tsx | 12 ------------ src/visits/helpers/MapModal.tsx | 7 +++---- test/servers/DeleteServerModal.test.tsx | 5 ++--- test/short-urls/helpers/EditMetaModal.test.tsx | 2 +- test/short-urls/helpers/EditShortUrlModal.test.tsx | 2 +- test/short-urls/helpers/EditTagsModal.test.tsx | 4 ++-- test/short-urls/helpers/QrCodeModal.test.tsx | 5 ++--- test/tags/helpers/DeleteTagConfirmModal.test.tsx | 5 ++--- test/visits/helpers/MapModal.test.tsx | 4 ++-- 20 files changed, 43 insertions(+), 79 deletions(-) delete mode 100644 src/utils/BlurredModal.scss delete mode 100644 src/utils/BlurredModal.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index a98b261e..1c39495e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), * [#379](https://github.com/shlinkio/shlink-web-client/issues/379) and [#384](https://github.com/shlinkio/shlink-web-client/issues/384) Improved QR code modal, including controls to customize size, format and margin, as well as a button to copy the link to the clipboard. * [#385](https://github.com/shlinkio/shlink-web-client/issues/385) Added setting to determine if "validate URL" should be enabled or disabled by default. * [#386](https://github.com/shlinkio/shlink-web-client/issues/386) Added new card in overview section to display amount of orphan visits when using Shlink 2.6.0 or higher. -* [#177](https://github.com/shlinkio/shlink-web-client/issues/177) Added dark theme. **[BETA]** +* [#177](https://github.com/shlinkio/shlink-web-client/issues/177) Added dark theme. ### Changed * *Nothing* diff --git a/src/servers/DeleteServerModal.tsx b/src/servers/DeleteServerModal.tsx index 2c2c2eee..4d74869e 100644 --- a/src/servers/DeleteServerModal.tsx +++ b/src/servers/DeleteServerModal.tsx @@ -1,6 +1,5 @@ -import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { RouterProps } from 'react-router'; -import { BlurredModal } from '../utils/BlurredModal'; import { ServerWithId } from './data'; export interface DeleteServerModalProps { @@ -21,7 +20,7 @@ const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }: De }; return ( - + Remove server

Are you sure you want to remove {server ? server.name : ''}?

@@ -36,7 +35,7 @@ const DeleteServerModal = ({ server, toggle, isOpen, deleteServer, history }: De -
+ ); }; diff --git a/src/short-urls/UseExistingIfFoundInfoIcon.tsx b/src/short-urls/UseExistingIfFoundInfoIcon.tsx index 3db7c9b1..2d003a1e 100644 --- a/src/short-urls/UseExistingIfFoundInfoIcon.tsx +++ b/src/short-urls/UseExistingIfFoundInfoIcon.tsx @@ -1,12 +1,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; -import { ModalBody, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalHeader } from 'reactstrap'; import { useToggle } from '../utils/helpers/hooks'; -import { BlurredModal } from '../utils/BlurredModal'; import './UseExistingIfFoundInfoIcon.scss'; const InfoModal = ({ isOpen, toggle }: { isOpen: boolean; toggle: () => void }) => ( - + Info

@@ -34,7 +33,7 @@ const InfoModal = ({ isOpen, toggle }: { isOpen: boolean; toggle: () => void }) - + ); const UseExistingIfFoundInfoIcon = () => { diff --git a/src/short-urls/helpers/DeleteShortUrlModal.tsx b/src/short-urls/helpers/DeleteShortUrlModal.tsx index 621a03fb..435b3318 100644 --- a/src/short-urls/helpers/DeleteShortUrlModal.tsx +++ b/src/short-urls/helpers/DeleteShortUrlModal.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { identity, pipe } from 'ramda'; import { ShortUrlDeletion } from '../reducers/shortUrlDeletion'; import { ShortUrlModalProps } from '../data'; @@ -7,7 +7,6 @@ import { handleEventPreventingDefault, OptionalString } from '../../utils/utils' import { Result } from '../../utils/Result'; import { isInvalidDeletionError } from '../../api/utils'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; interface DeleteShortUrlModalConnectProps extends ShortUrlModalProps { shortUrlDeletion: ShortUrlDeletion; @@ -33,7 +32,7 @@ const DeleteShortUrlModal = ( }); return ( - +

Delete short URL @@ -68,7 +67,7 @@ const DeleteShortUrlModal = ( -
+ ); }; diff --git a/src/short-urls/helpers/EditMetaModal.tsx b/src/short-urls/helpers/EditMetaModal.tsx index 8b8d1b4c..688cf9be 100644 --- a/src/short-urls/helpers/EditMetaModal.tsx +++ b/src/short-urls/helpers/EditMetaModal.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, useState } from 'react'; -import { ModalBody, ModalFooter, ModalHeader, FormGroup, Input, UncontrolledTooltip } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader, FormGroup, Input, UncontrolledTooltip } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; import { ExternalLink } from 'react-external-link'; @@ -12,7 +12,6 @@ import { ShortUrl, ShortUrlMeta, ShortUrlModalProps } from '../data'; import { handleEventPreventingDefault, Nullable, OptionalString } from '../../utils/utils'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; interface EditMetaModalConnectProps extends ShortUrlModalProps { shortUrlMeta: ShortUrlMetaEdition; @@ -43,7 +42,7 @@ const EditMetaModal = ( }).then(close); return ( - + Edit metadata for @@ -95,7 +94,7 @@ const EditMetaModal = ( - + ); }; diff --git a/src/short-urls/helpers/EditShortUrlModal.tsx b/src/short-urls/helpers/EditShortUrlModal.tsx index cee662d4..0a09e27e 100644 --- a/src/short-urls/helpers/EditShortUrlModal.tsx +++ b/src/short-urls/helpers/EditShortUrlModal.tsx @@ -1,12 +1,11 @@ import { useState } from 'react'; -import { ModalBody, ModalFooter, ModalHeader, FormGroup, Input, Button } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader, FormGroup, Input, Button } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import { ShortUrlEdition } from '../reducers/shortUrlEdition'; import { handleEventPreventingDefault, hasValue, OptionalString } from '../../utils/utils'; import { ShortUrlModalProps } from '../data'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; interface EditShortUrlModalProps extends ShortUrlModalProps { shortUrlEdition: ShortUrlEdition; @@ -21,7 +20,7 @@ const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShor const doEdit = async () => editShortUrl(shortUrl.shortCode, shortUrl.domain, longUrl).then(toggle); return ( - + Edit long URL for @@ -50,7 +49,7 @@ const EditShortUrlModal = ({ isOpen, toggle, shortUrl, shortUrlEdition, editShor - + ); }; diff --git a/src/short-urls/helpers/EditTagsModal.tsx b/src/short-urls/helpers/EditTagsModal.tsx index bc6875ed..a390499c 100644 --- a/src/short-urls/helpers/EditTagsModal.tsx +++ b/src/short-urls/helpers/EditTagsModal.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import { ShortUrlTags } from '../reducers/shortUrlTags'; import { ShortUrlModalProps } from '../data'; @@ -7,7 +7,6 @@ import { OptionalString } from '../../utils/utils'; import { TagsSelectorProps } from '../../tags/helpers/TagsSelector'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; interface EditTagsModalProps extends ShortUrlModalProps { shortUrlTags: ShortUrlTags; @@ -29,7 +28,7 @@ const EditTagsModal = (TagsSelector: FC) => ( .catch(() => {}); return ( - + Edit tags for @@ -47,7 +46,7 @@ const EditTagsModal = (TagsSelector: FC) => ( {saving ? 'Saving tags...' : 'Save tags'} - + ); }; diff --git a/src/short-urls/helpers/QrCodeModal.tsx b/src/short-urls/helpers/QrCodeModal.tsx index 9b1c7bad..4bde939e 100644 --- a/src/short-urls/helpers/QrCodeModal.tsx +++ b/src/short-urls/helpers/QrCodeModal.tsx @@ -1,5 +1,5 @@ import { useMemo, useState } from 'react'; -import { DropdownItem, FormGroup, ModalBody, ModalHeader, Row } from 'reactstrap'; +import { Modal, DropdownItem, FormGroup, ModalBody, ModalHeader, Row } from 'reactstrap'; import { ExternalLink } from 'react-external-link'; import classNames from 'classnames'; import { ShortUrlModalProps } from '../data'; @@ -9,7 +9,6 @@ import { DropdownBtn } from '../../utils/DropdownBtn'; import { CopyToClipboardIcon } from '../../utils/CopyToClipboardIcon'; import { buildQrCodeUrl, QrCodeCapabilities, QrCodeFormat } from '../../utils/helpers/qrCodes'; import './QrCodeModal.scss'; -import { BlurredModal } from '../../utils/BlurredModal'; interface QrCodeModalConnectProps extends ShortUrlModalProps { selectedServer: ReachableServer; @@ -38,7 +37,7 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }: }, [ totalSize ]); return ( - + QR code for {shortUrl} @@ -99,7 +98,7 @@ const QrCodeModal = ({ shortUrl: { shortUrl }, toggle, isOpen, selectedServer }:
{size}x{size}
-
+ ); }; diff --git a/src/tags/helpers/DeleteTagConfirmModal.tsx b/src/tags/helpers/DeleteTagConfirmModal.tsx index dc4d2838..c4ef46ab 100644 --- a/src/tags/helpers/DeleteTagConfirmModal.tsx +++ b/src/tags/helpers/DeleteTagConfirmModal.tsx @@ -1,9 +1,8 @@ -import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { TagDeletion } from '../reducers/tagDelete'; import { TagModalProps } from '../data'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; interface DeleteTagConfirmModalProps extends TagModalProps { deleteTag: (tag: string) => Promise; @@ -22,7 +21,7 @@ const DeleteTagConfirmModal = ( }; return ( - + Delete tag @@ -40,7 +39,7 @@ const DeleteTagConfirmModal = ( {deleting ? 'Deleting tag...' : 'Delete tag'} - + ); }; diff --git a/src/tags/helpers/EditTagModal.tsx b/src/tags/helpers/EditTagModal.tsx index 938d8b6d..7cbd5169 100644 --- a/src/tags/helpers/EditTagModal.tsx +++ b/src/tags/helpers/EditTagModal.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { ModalBody, ModalFooter, ModalHeader, Popover } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader, Popover } from 'reactstrap'; import { ChromePicker } from 'react-color'; import { faPalette as colorIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -10,7 +10,6 @@ import { TagModalProps } from '../data'; import { TagEdition } from '../reducers/tagEdit'; import { Result } from '../../utils/Result'; import { ShlinkApiError } from '../../api/ShlinkApiError'; -import { BlurredModal } from '../../utils/BlurredModal'; import './EditTagModal.scss'; interface EditTagModalProps extends TagModalProps { @@ -32,7 +31,7 @@ const EditTagModal = ({ getColorForKey }: ColorGenerator) => ( .catch(() => {})); return ( - +
Edit tag @@ -69,7 +68,7 @@ const EditTagModal = ({ getColorForKey }: ColorGenerator) => ( -
+ ); }; diff --git a/src/utils/BlurredModal.scss b/src/utils/BlurredModal.scss deleted file mode 100644 index 66d4e435..00000000 --- a/src/utils/BlurredModal.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import './base'; - -@media (min-width: $smMin) { - #root { - transition: filter 300ms; - } - - .with-modal #root { - filter: blur(1.5px); - } -} diff --git a/src/utils/BlurredModal.tsx b/src/utils/BlurredModal.tsx deleted file mode 100644 index 62f68373..00000000 --- a/src/utils/BlurredModal.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { FC } from 'react'; -import { ModalProps, Modal } from 'reactstrap'; // eslint-disable-line import/named -import './BlurredModal.scss'; - -const onEnter = () => document.body.classList.add('with-modal'); -const onExit = () => document.body.classList.remove('with-modal'); - -export const BlurredModal: FC = ({ children, ...rest }) => ( - - {children} - -); diff --git a/src/visits/helpers/MapModal.tsx b/src/visits/helpers/MapModal.tsx index 4d29be2a..ae57ed12 100644 --- a/src/visits/helpers/MapModal.tsx +++ b/src/visits/helpers/MapModal.tsx @@ -1,9 +1,8 @@ import { FC } from 'react'; -import { ModalBody } from 'reactstrap'; +import { Modal, ModalBody } from 'reactstrap'; import { MapContainer, TileLayer, Marker, Popup, MapContainerProps } from 'react-leaflet'; import { prop } from 'ramda'; import { CityStats } from '../types'; -import { BlurredModal } from '../../utils/BlurredModal'; import './MapModal.scss'; interface MapModalProps { @@ -37,7 +36,7 @@ const calculateMapProps = (locations: CityStats[]): MapContainerProps => { }; const MapModal = ({ toggle, isOpen, title, locations = [] }: MapModalProps) => ( - +

{title} @@ -52,7 +51,7 @@ const MapModal = ({ toggle, isOpen, title, locations = [] }: MapModalProps) => ( ))} - + ); export default MapModal; diff --git a/test/servers/DeleteServerModal.test.tsx b/test/servers/DeleteServerModal.test.tsx index fc844b32..14eed177 100644 --- a/test/servers/DeleteServerModal.test.tsx +++ b/test/servers/DeleteServerModal.test.tsx @@ -1,10 +1,9 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'; import { History } from 'history'; import { Mock } from 'ts-mockery'; import DeleteServerModal from '../../src/servers/DeleteServerModal'; import { ServerWithId } from '../../src/servers/data'; -import { BlurredModal } from '../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -28,7 +27,7 @@ describe('', () => { afterEach(jest.clearAllMocks); it('renders a modal window', () => { - expect(wrapper.find(BlurredModal)).toHaveLength(1); + expect(wrapper.find(Modal)).toHaveLength(1); expect(wrapper.find(ModalHeader)).toHaveLength(1); expect(wrapper.find(ModalBody)).toHaveLength(1); expect(wrapper.find(ModalFooter)).toHaveLength(1); diff --git a/test/short-urls/helpers/EditMetaModal.test.tsx b/test/short-urls/helpers/EditMetaModal.test.tsx index b06f2a40..b5553125 100644 --- a/test/short-urls/helpers/EditMetaModal.test.tsx +++ b/test/short-urls/helpers/EditMetaModal.test.tsx @@ -71,7 +71,7 @@ describe('', () => { it.each([ [ '.btn-link', 'onClick' ], - [ 'BlurredModal', 'toggle' ], + [ 'Modal', 'toggle' ], [ 'ModalHeader', 'toggle' ], ])('resets meta when modal is toggled in any way', (componentToFind, propToCall) => { const wrapper = createWrapper({ saving: false, error: false }); diff --git a/test/short-urls/helpers/EditShortUrlModal.test.tsx b/test/short-urls/helpers/EditShortUrlModal.test.tsx index 0400d379..2ab14c4b 100644 --- a/test/short-urls/helpers/EditShortUrlModal.test.tsx +++ b/test/short-urls/helpers/EditShortUrlModal.test.tsx @@ -67,7 +67,7 @@ describe('', () => { it.each([ [ '[color="link"]', 'onClick' ], - [ 'BlurredModal', 'toggle' ], + [ 'Modal', 'toggle' ], [ 'ModalHeader', 'toggle' ], ])('toggles modal with different mechanisms', (componentToFind, propToCall) => { const wrapper = createWrapper({}, { saving: false, error: false }); diff --git a/test/short-urls/helpers/EditTagsModal.test.tsx b/test/short-urls/helpers/EditTagsModal.test.tsx index bbff8bb7..fddccad0 100644 --- a/test/short-urls/helpers/EditTagsModal.test.tsx +++ b/test/short-urls/helpers/EditTagsModal.test.tsx @@ -4,7 +4,7 @@ import createEditTagsModal from '../../../src/short-urls/helpers/EditTagsModal'; import { ShortUrl } from '../../../src/short-urls/data'; import { ShortUrlTags } from '../../../src/short-urls/reducers/shortUrlTags'; import { OptionalString } from '../../../src/utils/utils'; -import { BlurredModal } from '../../../src/utils/BlurredModal'; +import { Modal } from 'reactstrap'; describe('', () => { let wrapper: ShallowWrapper; @@ -98,7 +98,7 @@ describe('', () => { saving: false, error: false, }); - const modal = wrapper.find(BlurredModal); + const modal = wrapper.find(Modal); modal.simulate('closed'); expect(editShortUrlTags).not.toHaveBeenCalled(); diff --git a/test/short-urls/helpers/QrCodeModal.test.tsx b/test/short-urls/helpers/QrCodeModal.test.tsx index 67089d06..d84b5572 100644 --- a/test/short-urls/helpers/QrCodeModal.test.tsx +++ b/test/short-urls/helpers/QrCodeModal.test.tsx @@ -1,13 +1,12 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { ExternalLink } from 'react-external-link'; -import { ModalBody, ModalHeader, Row } from 'reactstrap'; +import { Modal, ModalBody, ModalHeader, Row } from 'reactstrap'; import { Mock } from 'ts-mockery'; import QrCodeModal from '../../../src/short-urls/helpers/QrCodeModal'; import { ShortUrl } from '../../../src/short-urls/data'; import { ReachableServer } from '../../../src/servers/data'; import { CopyToClipboardIcon } from '../../../src/utils/CopyToClipboardIcon'; import { DropdownBtn } from '../../../src/utils/DropdownBtn'; -import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -81,7 +80,7 @@ describe('', () => { expect(wrapper.find('.mt-2').text()).toEqual(`${size}x${size}`); expect(wrapper.find('label').at(0).text()).toEqual(`Size: ${size}px`); expect(wrapper.find('label').at(1).text()).toEqual(`Margin: ${margin}px`); - expect(wrapper.find(BlurredModal).prop('size')).toEqual(modalSize); + expect(wrapper.find(Modal).prop('size')).toEqual(modalSize); }); it.each([ diff --git a/test/tags/helpers/DeleteTagConfirmModal.test.tsx b/test/tags/helpers/DeleteTagConfirmModal.test.tsx index 090e31e4..866dadce 100644 --- a/test/tags/helpers/DeleteTagConfirmModal.test.tsx +++ b/test/tags/helpers/DeleteTagConfirmModal.test.tsx @@ -1,8 +1,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; -import { ModalBody, ModalFooter } from 'reactstrap'; +import { Modal, ModalBody, ModalFooter } from 'reactstrap'; import DeleteTagConfirmModal from '../../../src/tags/helpers/DeleteTagConfirmModal'; import { TagDeletion } from '../../../src/tags/reducers/tagDelete'; -import { BlurredModal } from '../../../src/utils/BlurredModal'; describe('', () => { let wrapper: ShallowWrapper; @@ -69,7 +68,7 @@ describe('', () => { it('does no further actions when modal is closed without deleting tag', () => { wrapper = createWrapper({ error: false, deleting: false }); - const modal = wrapper.find(BlurredModal); + const modal = wrapper.find(Modal); modal.simulate('closed'); expect(deleteTag).not.toHaveBeenCalled(); diff --git a/test/visits/helpers/MapModal.test.tsx b/test/visits/helpers/MapModal.test.tsx index da6c18ec..71cdda26 100644 --- a/test/visits/helpers/MapModal.test.tsx +++ b/test/visits/helpers/MapModal.test.tsx @@ -2,7 +2,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Marker, Popup } from 'react-leaflet'; import MapModal from '../../../src/visits/helpers/MapModal'; import { CityStats } from '../../../src/visits/types'; -import { BlurredModal } from '../../../src/utils/BlurredModal'; +import { Modal } from 'reactstrap'; describe('', () => { let wrapper: ShallowWrapper; @@ -33,7 +33,7 @@ describe('', () => { afterEach(() => wrapper.unmount()); it('renders modal with provided props', () => { - const modal = wrapper.find(BlurredModal); + const modal = wrapper.find(Modal); const header = wrapper.find('.map-modal__modal-title'); expect(modal.prop('toggle')).toEqual(toggle);