Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
digibib
deichman
Commits
a34ff190
Commit
a34ff190
authored
Feb 25, 2021
by
David Björkheim
Browse files
DEICH-5554
Deichman.no: imporved modal handling
parent
a3b14df1
Changes
10
Hide whitespace changes
Inline
Side-by-side
deichman.no/components/LoginButton/LoginButton.js
0 → 100644
View file @
a34ff190
import
{
Button
,
Modal
}
from
"
@digibib/deichman-ui
"
;
import
FocusTrap
from
"
focus-trap-react
"
;
import
React
,
{
useEffect
}
from
"
react
"
;
import
{
useState
}
from
"
react
"
;
import
LoginForm
from
"
../LoginForm
"
;
import
ModalPortal
from
"
../ModalPortal/ModalPortal
"
;
export
default
function
LoginButton
(
props
)
{
const
children
=
props
.
children
;
const
butttonsProps
=
{
...
props
};
delete
butttonsProps
.
children
;
const
[
showLoginModal
,
setShowLoginModal
]
=
useState
(
false
);
const
[
showDelayedModal
,
setShowDelayedModal
]
=
useState
(
false
);
useEffect
(
()
=>
{
setTimeout
(()
=>
setShowDelayedModal
(
showLoginModal
),
300
);
},
[
showLoginModal
]
);
return
(
<>
{
showLoginModal
&&
(
<
ModalPortal
>
<
FocusTrap
active
=
{
showDelayedModal
}
focusTrapOptions
=
{{
initialFocus
:
"
#login-trap
"
}}
>
<
Modal
name
=
"
Login
"
visible
=
{
showDelayedModal
}
onClose
=
{()
=>
setShowLoginModal
(
false
)}
showClose
>
<
div
className
=
"
focus-trap-target
"
id
=
"
login-trap
"
tabIndex
=
"
-1
"
>
<
LoginForm
title
=
"
Logg inn
"
fullWidth
preventRedirect
fetchFavouritesOnLogin
fetchKohaBranchesOnLogin
showCancel
onCancel
=
{()
=>
setShowLoginModal
(
false
)}
/
>
<
/div
>
<
/Modal
>
<
/FocusTrap
>
<
/ModalPortal
>
)}
<
Button
{...
butttonsProps
}
onClick
=
{()
=>
setShowLoginModal
(
true
)}
>
{
children
}
<
/Button
>
<
/
>
);
}
deichman.no/components/ModalPortal/ModalPortal.js
0 → 100644
View file @
a34ff190
import
React
,
{
useRef
,
useEffect
}
from
"
react
"
;
import
ReactDOM
from
"
react-dom
"
;
import
{
setBodyFreeze
}
from
"
../../utilities/accessibility
"
;
export
default
function
ModalPortal
({
children
})
{
const
el
=
useRef
(
document
.
createElement
(
"
div
"
));
useEffect
(()
=>
{
const
modalRoot
=
document
.
getElementById
(
"
modalRoot
"
);
modalRoot
.
appendChild
(
el
.
current
);
setBodyFreeze
(
true
);
return
()
=>
{
setBodyFreeze
(
false
);
modalRoot
&&
el
.
current
&&
modalRoot
.
removeChild
(
el
.
current
);
};
},
[]);
return
<>
{
ReactDOM
.
createPortal
(
children
,
el
.
current
)}
<
/>
;
}
deichman.no/components/RemoteReservationForm/RemoteReservationForm.js
View file @
a34ff190
import
React
,
{
Fragment
}
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
import
LoginForm
from
"
../LoginForm
"
;
import
{
Block
,
Button
,
Input
,
Hr
}
from
"
@digibib/deichman-ui
"
;
const
RemoteReservationForm
=
({
isLoggedIn
,
onSubmit
,
onClose
,
completed
,
...
...
@@ -15,27 +12,8 @@ const RemoteReservationForm = ({
borrowerId
,
onChangeBorrowerId
,
numHolds
,
recordId
,
publicationTitle
publication
=
{}
})
=>
{
// Return login form if not authorised
if
(
!
isLoggedIn
)
{
return
(
<
LoginForm
title
=
"
Logg inn for å reservere
"
fullWidth
preventRedirect
fetchFavouritesOnLogin
redirectModalData
=
{{
type
:
"
reservation
"
,
recordId
}}
showCancel
onCancel
=
{
onClose
}
/
>
);
}
// Return success or error message if reservation completed
if
(
completed
)
{
const
header
=
error
...
...
@@ -66,6 +44,7 @@ const RemoteReservationForm = ({
);
}
const
{
mainTitle
}
=
publication
;
return
(
<
form
onSubmit
=
{
onSubmit
}
>
<
Block
top
=
{
4
}
>
...
...
@@ -74,7 +53,7 @@ const RemoteReservationForm = ({
<
Block
top
=
{
6
}
>
<
p
>
<
strong
>
{
publicatio
nTitle
}
<
/strong
>
<
strong
>
{
mai
nTitle
}
<
/strong
>
<
/p
>
{
numHolds
>
0
&&
(
<
p
>
...
...
deichman.no/components/ReservationForm/ReservationForm.js
View file @
a34ff190
import
React
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
import
{
connect
}
from
"
react-redux
"
;
import
"
./styles.css
"
;
import
LoginForm
from
"
../LoginForm
"
;
import
{
Block
,
Button
,
Select
,
Hr
}
from
"
@digibib/deichman-ui
"
;
import
{
translations
as
TRANSLATIONS
}
from
"
../../constants/translations
"
;
import
PublicationImage
from
"
../PublicationImage
"
;
import
{
getWaitingPeriodText
}
from
"
../../utilities/datetime
"
;
import
{
sortBy
}
from
"
lodash
"
;
const
ReservationForm
=
({
isLoggedIn
,
import
"
./styles.css
"
;
export
default
function
ReservationForm
({
allBranches
,
selectedBranchId
,
onChangeBranch
,
...
...
@@ -23,36 +21,7 @@ const ReservationForm = ({
error
,
reservationWork
,
onRedirectClose
})
=>
{
// Return login form if not authorised
if
(
!
isLoggedIn
)
{
return
(
<
LoginForm
title
=
"
Logg inn for å reservere
"
fullWidth
preventRedirect
fetchFavouritesOnLogin
fetchKohaBranchesOnLogin
showCancel
onCancel
=
{
onClose
}
/
>
);
}
const
renderQueueInformation
=
()
=>
{
const
currentItem
=
reservationWork
?.
currentItem
;
const
priority
=
currentItem
?.
pri
;
const
estimate
=
currentItem
?.
estimate
?.
estimate
;
if
(
priority
&&
estimate
)
{
return
(
<
p
>
Du
er
nummer
{
priority
}
i
køen
({
getWaitingPeriodText
(
estimate
)}).
<
/p
>
);
}
};
})
{
// Return success message if reservation completed
if
(
completed
&&
!
error
)
{
const
{
mainTitle
,
mediaType
,
sellingPoint
,
imageObj
}
=
publication
;
...
...
@@ -74,21 +43,17 @@ const ReservationForm = ({
<
/div
>
<
div
className
=
"
reservation-success-form__card-text
"
>
<
Block
top
=
{
0
}
>
{
renderQueueInformation
()}
{
renderQueueInformation
(
reservationWork
)}
<
p
>
Du
vil
få
beskjed
når
bestillingen
er
klar
for
henting
.
<
/p
>
<
br
/>
<
p
>
Husk
at
du
alltid
kan
se
og
administrere
<
/p
>
<
p
>
bestillingene
dine
på
{
"
"
}
<
p
className
=
"
my_page-info
"
>
Husk
at
du
alltid
kan
se
og
administrere
bestillingene
dine
på
{
"
"
}
<
a
className
=
"
link
"
onClick
=
{
onRedirectClose
}
>
Min
side
.
<
/a
>
<
/p
>
<
/Block
>
<
Block
top
=
{
4
}
>
<
Hr
/>
<
/Block
>
<
Hr
/>
<
Block
className
=
"
reservation-success-form__card-button
"
top
=
{
4
}
>
<
Button
full
primary
onClick
=
{
onClose
}
>
...
...
@@ -101,17 +66,7 @@ const ReservationForm = ({
);
}
// TEMP: Remove Hovedbiblioteket from available pick-up branches
let
branches
=
allBranches
.
filter
(
b
=>
!
b
.
tempClosedForPickup
)
.
filter
(
b
=>
b
.
value
!==
"
hutl
"
);
if
(
limitedToBranches
.
length
>
0
)
{
branches
=
branches
.
filter
(
branch
=>
limitedToBranches
.
includes
(
branch
.
value
)
);
}
const
sortedBranches
=
sortBy
(
branches
,
[
"
text
"
]);
const
sortedBranches
=
sortBy
(
allBranches
,
[
"
text
"
]);
return
(
<
form
onSubmit
=
{
onSubmit
}
>
...
...
@@ -167,18 +122,16 @@ const ReservationForm = ({
<
/Block
>
<
/form
>
);
}
;
}
ReservationForm
.
defaultProps
=
{
error
:
false
,
recordId
:
""
,
limitedToBranches
:
[]
};
ReservationForm
.
propTypes
=
{
isLoggedIn
:
PropTypes
.
bool
.
isRequired
,
allBranches
:
PropTypes
.
array
.
isRequired
,
recordId
:
PropTypes
.
string
,
selectedBranchId
:
PropTypes
.
oneOfType
([
PropTypes
.
string
,
PropTypes
.
bool
])
.
isRequired
,
onChangeBranch
:
PropTypes
.
func
.
isRequired
,
...
...
@@ -189,19 +142,21 @@ ReservationForm.propTypes = {
error
:
PropTypes
.
oneOfType
([
PropTypes
.
object
,
PropTypes
.
bool
]),
limitedToBranches
:
PropTypes
.
array
,
reservationWork
:
PropTypes
.
object
.
isRequired
,
workData
:
PropTypes
.
object
.
isRequired
,
onRedirectClose
:
PropTypes
.
func
.
isRequired
};
const
mapStateToProps
=
state
=>
{
const
{
work
}
=
state
.
resources
;
// ----------- Utils below -------------
return
{
workData
:
w
ork
.
currentItem
}
;
}
;
function
renderQueueInformation
(
reservationWork
)
{
const
currentItem
=
reservationW
ork
?
.
currentItem
;
const
priority
=
currentItem
?.
pri
;
const
estimate
=
currentItem
?.
estimate
?.
estimate
;
export
default
connect
(
mapStateToProps
,
{}
)(
ReservationForm
);
if
(
priority
&&
estimate
)
{
return
(
<
p
>
Du
er
nummer
{
priority
}
i
køen
({
getWaitingPeriodText
(
estimate
)}).
<
/p
>
);
}
}
deichman.no/components/ReservationForm/styles.css
View file @
a34ff190
.reservation-success-form__card
{
margin-top
:
var
(
--spacing-6
);
margin-top
:
var
(
--spacing-6
);
}
/* On screens that are 650px or more, set display to horizontal */
@media
screen
and
(
min-width
:
650px
)
{
.reservation-success-form
{
margin-bottom
:
var
(
--spacing-4
);
}
.reservation-success-form
{
margin-bottom
:
var
(
--spacing-4
);
}
.reservation-success-form__card
{
display
:
flex
;
}
.reservation-success-form__card
{
display
:
flex
;
}
.reservation-success-form__card-text
{
padding-left
:
var
(
--spacing-6
);
position
:
relative
;
.reservation-success-form__card-text
{
padding-left
:
var
(
--spacing-6
);
display
:
flex
;
flex-direction
:
column
;
&
>
*
{
margin-top
:
var
(
--spacing-4
);
&:first-child
{
margin-top
:
0
;
flex-grow
:
1
;
}
}
.reservation-success-form__card-button
{
position
:
absolute
;
bottom
:
0
;
left
:
0
;
right
:
0
;
padding-left
:
var
(
--spacing-6
);
padding-bottom
:
var
(
--spacing-1
);
&
.my_page-info
{
margin-top
:
1.5rem
;
}
}
}
@media
screen
and
(
max-width
:
649px
)
{
.reservation-success-form__card-text
{
margin-top
:
var
(
--spacing-6
);
}
}
\ No newline at end of file
.reservation-success-form__card-text
{
margin-top
:
var
(
--spacing-6
);
}
}
deichman.no/components/ReservationsContainer/ReservationContainer.js
View file @
a34ff190
import
React
,
{
Fragment
}
from
"
react
"
;
import
ReactDOM
from
"
react-dom
"
;
import
PropTypes
from
"
prop-types
"
;
import
autoBind
from
"
auto-bind
"
;
import
{
connect
}
from
"
react-redux
"
;
...
...
@@ -22,9 +21,8 @@ import RemoteReservationForm from "../RemoteReservationForm";
import
{
Modal
}
from
"
@digibib/deichman-ui
"
;
import
"
./styles.css
"
;
import
ReservationSelector
from
"
./ReservationSelector
"
;
import
{
setBodyFreeze
}
from
"
../../utilities/accessibility
"
;
import
ModalPortal
from
"
../ModalPortal/ModalPortal
"
;
let
modalRoot
;
/**
* Responsible for reserving items from the catalog
* -> Shows loginform if user is not logged in
...
...
@@ -50,19 +48,16 @@ class ReservationContainer extends React.Component {
nextProps
.
homeBranch
&&
nextProps
.
branches
.
length
>
0
)
{
// Check if homeBranch is val
u
d
// Check if homeBranch is val
i
d
const
homeBranchIsValid
=
nextProps
.
branches
.
some
(
b
=>
b
.
value
===
nextProps
.
homeBranch
&&
!
b
.
tempClosedForPickup
&&
b
.
value
!==
"
hutl
"
b
=>
b
.
value
===
nextProps
.
homeBranch
&&
!
b
.
tempClosedForPickup
);
// default to
Hovedbiblioteket
if homebranch
// default to
Bjørvika
if homebranch
// is missing, unknown or closed for pickups
const
selectedBranchId
=
homeBranchIsValid
?
nextProps
.
homeBranch
:
"
fmaj
"
;
:
"
bjor
"
;
return
{
selectedBranchId
...
...
@@ -73,11 +68,8 @@ class ReservationContainer extends React.Component {
}
componentDidMount
()
{
setBodyFreeze
(
true
);
this
.
props
.
fetchKohaBranches
();
setTimeout
(()
=>
this
.
setState
({
show
:
true
}),
100
);
modalRoot
=
document
.
getElementById
(
"
modalRoot
"
);
modalRoot
.
appendChild
(
this
.
el
);
}
componentDidUpdate
()
{
...
...
@@ -87,11 +79,6 @@ class ReservationContainer extends React.Component {
}
}
componentWillUnmount
()
{
setBodyFreeze
(
false
);
modalRoot
&&
modalRoot
.
removeChild
(
this
.
el
);
}
handleChangeBranch
(
e
)
{
this
.
setState
({
selectedBranchId
:
e
.
target
.
value
...
...
@@ -197,76 +184,81 @@ class ReservationContainer extends React.Component {
limitedToBranches
=
items
.
map
(
item
=>
item
.
branchcode
);
}
return
ReactDOM
.
createPortal
(
<
div
className
=
"
reservation-wrapper
"
>
<
FocusTrap
active
=
{
this
.
state
.
show
}
focusTrapOptions
=
{{
initialFocus
:
"
#reservation-trap
"
}}
>
<
Modal
name
=
"
Reserver
"
visible
=
{
this
.
state
.
show
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
sizeW
=
"
40rem
"
showClose
const
availableBranches
=
limitedToBranches
.
length
>
0
?
branches
.
filter
(
branch
=>
limitedToBranches
.
includes
(
branch
.
value
))
:
branches
;
return
(
<
ModalPortal
>
<
div
className
=
"
reservation-wrapper
"
>
<
FocusTrap
active
=
{
this
.
state
.
show
}
focusTrapOptions
=
{{
initialFocus
:
"
#reservation-trap
"
}}
>
<
div
className
=
"
focus-trap-target reservation-content
"
id
=
"
reservation-trap
"
tabIndex
=
"
-1
"
<
Modal
name
=
"
Reserver
"
visible
=
{
this
.
state
.
show
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
sizeW
=
"
40rem
"
showClose
>
<
Fragment
>
{
!
publication
&&
(
<
ReservationSelector
onClick
=
{
this
.
setPublication
}
copies
=
{
copies
}
publications
=
{
publications
}
currentPublication
=
{
this
.
props
.
currentPublication
}
/
>
)}
{
publication
&&
(
<>
{
isRemoteReservation
?
(
<
RemoteReservationForm
borrowerId
=
{
borrowerId
}
onChangeBorrowerId
=
{
this
.
handleChangeBorrowerId
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
isSubmitting
=
{
reservationInProgress
}
isLoggedIn
=
{
isLoggedIn
}
completed
=
{
reservationCompleted
}
error
=
{
reservationError
}
recordId
=
{
recordId
}
numHolds
=
{
`
${
Number
(
numHolds
)}
`
}
publicationTitle
=
{
title
}
onSubmit
=
{
this
.
handleSubmit
}
/
>
)
:
(
<
ReservationForm
allBranches
=
{
branches
}
selectedBranchId
=
{
selectedBranchId
}
onChangeBranch
=
{
this
.
handleChangeBranch
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
isSubmitting
=
{
reservationInProgress
}
isLoggedIn
=
{
isLoggedIn
}
completed
=
{
reservationCompleted
}
error
=
{
reservationError
}
publication
=
{
publication
}
limitedToBranches
=
{
limitedToBranches
}
onSubmit
=
{
this
.
handleSubmit
}
reservationWork
=
{
work
}
onRedirectClose
=
{
this
.
handleCloseModal
(
true
)}
/
>
)}
<
/
>
)}
<
/Fragment
>
<
/div
>
<
/Modal
>
<
/FocusTrap
>
<
/div>
,
this
.
el
<
div
className
=
"
focus-trap-target reservation-content
"
id
=
"
reservation-trap
"
tabIndex
=
"
-1
"
>
<
Fragment
>
{
!
publication
&&
(
<
ReservationSelector
onClick
=
{
this
.
setPublication
}
copies
=
{
copies
}
publications
=
{
publications
}
currentPublication
=
{
this
.
props
.
currentPublication
}
/
>
)}
{
publication
&&
(
<>
{
isRemoteReservation
?
(
<
RemoteReservationForm
borrowerId
=
{
borrowerId
}
onChangeBorrowerId
=
{
this
.
handleChangeBorrowerId
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
isSubmitting
=
{
reservationInProgress
}
isLoggedIn
=
{
isLoggedIn
}
completed
=
{
reservationCompleted
}
error
=
{
reservationError
}
recordId
=
{
recordId
}
numHolds
=
{
`
${
Number
(
numHolds
)}
`
}
publicationTitle
=
{
title
}
onSubmit
=
{
this
.
handleSubmit
}
/
>
)
:
(
<
ReservationForm
allBranches
=
{
availableBranches
}
selectedBranchId
=
{
selectedBranchId
}
onChangeBranch
=
{
this
.
handleChangeBranch
}
onClose
=
{
this
.
handleCloseModal
(
false
)}
isSubmitting
=
{
reservationInProgress
}
isLoggedIn
=
{
isLoggedIn
}
completed
=
{
reservationCompleted
}
error
=
{
reservationError
}
publication
=
{
publication
}
limitedToBranches
=
{
limitedToBranches
}
onSubmit
=
{
this
.
handleSubmit
}
reservationWork
=
{
work
}
onRedirectClose
=
{
this
.
handleCloseModal
(
true
)}
/
>
)}
<
/
>
)}
<
/Fragment
>
<
/div
>
<
/Modal
>
<
/FocusTrap
>
<
/div
>
<
/ModalPortal
>
);
}
}
...
...
deichman.no/components/ReservePublicationWidget/ReservPublicationWidget.js
View file @
a34ff190
...
...
@@ -37,6 +37,8 @@ import { matchMedia } from "../../utilities/media";
import
{
uniqBy
}
from
"
lodash
"
;
import
ReservationContainer
from
"
../ReservationsContainer/ReservationContainer
"
;
import
{
useEffect
}
from
"
react
"
;
import
{
useSelector
}
from
"
react-redux
"
;
import
LoginButton
from
"
../LoginButton/LoginButton
"
;
export
default
function
ReservePublicationWidget
({
publications
,
...
...
@@ -46,9 +48,11 @@ export default function ReservePublicationWidget({
const
[
language
,
setLanguage
]
=
useState
(
languages
?.[
0
]);
const
[
format
,
setFormat
]
=
useState
(
formats
?.[
0
]);
const
[
platform
,
setPlatform
]
=
useState
(
platforms
?.[
0
]);
const
[
copies
,
setCopies
]
=
useState
({});
const
[
reservationPublications
,
setReservationPublications
]
=
useState
([]);
const
isLoggedIn
=
useSelector
(
state
=>
state
.
auth
.
isLoggedIn
);
const
mediaType
=
currentPublication
.
mediaType
;
const
title
=
matchMedia
(
titles
,
mediaType
);
const
showFields
=
matchMedia
(
availableFields
,
mediaType
);
...
...
@@ -137,29 +141,38 @@ export default function ReservePublicationWidget({
return
(
<
div
className
=
"
reserve-work-widget
"
>
<
h3
>
{
title
}
<
/h3
>
<
div
>
{
reserveChoices
.
map
(
choice
=>
{