Merged in feat/SW-2038-static-map-button (pull request #2715)
feat(SW-2038): refactor and create wrapper for static map and button * feat(SW-2038): refactor and create wrapper for static map and button * feature: use button from design-system over creating a new one * remove unused fragment * fix(SW-2038): add removed css * fix(SW-2038): update fake button component * fix(SW-2038): move FakeButton to design system Approved-by: Erik Tiekstra Approved-by: Joakim Jäderberg
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import styles from "./mapButton.module.css"
|
||||
|
||||
interface MapButtonProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function MapButton({ className = "" }: MapButtonProps) {
|
||||
const intl = useIntl()
|
||||
const params = useParams()
|
||||
const [mapUrl, setMapUrl] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href)
|
||||
url.searchParams.set("view", "map")
|
||||
setMapUrl(url.toString())
|
||||
}, [params])
|
||||
|
||||
if (!mapUrl) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<ButtonLink
|
||||
href={mapUrl}
|
||||
variant="Primary"
|
||||
color="Inverted"
|
||||
size="Small"
|
||||
typography="Body/Supporting text (caption)/smBold"
|
||||
className={`${className} ${styles.button}`}
|
||||
>
|
||||
<MaterialIcon icon="map" color="CurrentColor" />
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "See on map",
|
||||
})}
|
||||
</ButtonLink>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.button {
|
||||
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { MapWithButtonWrapper } from "@/components/Maps/MapWithButtonWrapper"
|
||||
|
||||
import styles from "./mapWrapper.module.css"
|
||||
|
||||
export default function MapWrapper({ children }: React.PropsWithChildren) {
|
||||
const params = useParams()
|
||||
const [mapUrl, setMapUrl] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href)
|
||||
url.searchParams.set("view", "map")
|
||||
setMapUrl(url.toString())
|
||||
}, [params])
|
||||
|
||||
if (!mapUrl) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Link className={styles.link} href={mapUrl}>
|
||||
<MapWithButtonWrapper>{children}</MapWithButtonWrapper>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1367px) {
|
||||
.link {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import StaticMap from "@/components/Maps/StaticMap"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import MapButton from "./MapButton"
|
||||
|
||||
import styles from "./staticMap.module.css"
|
||||
import MapWrapper from "./MapWrapper"
|
||||
|
||||
import type { MapLocation } from "@/types/components/mapLocation"
|
||||
|
||||
@@ -43,7 +41,7 @@ export default async function DestinationStaticMap({
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<div className={styles.mapWrapper}>
|
||||
<MapWrapper>
|
||||
<StaticMap
|
||||
country={country}
|
||||
city={city}
|
||||
@@ -53,7 +51,6 @@ export default async function DestinationStaticMap({
|
||||
zoomLevel={getZoomLevel(location?.default_zoom, !!country)}
|
||||
altText={altText}
|
||||
/>
|
||||
<MapButton className={styles.button} />
|
||||
</div>
|
||||
</MapWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
.mapWrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mapWrapper::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(31, 28, 27, 0);
|
||||
transition: background-color 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mapWrapper:hover::before {
|
||||
background-color: rgba(31, 28, 27, 0.1);
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
right: var(--Space-x2);
|
||||
bottom: var(--Space-x2);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.mapWrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Preamble from "@scandic-hotels/design-system/Preamble"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { useHotelFilterStore } from "@/stores/hotel-filters"
|
||||
|
||||
@@ -11,13 +11,15 @@ export default function HotelCount() {
|
||||
const resultCount = useHotelFilterStore((state) => state.resultCount)
|
||||
|
||||
return (
|
||||
<Preamble>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{amount, plural, one {# hotel} other {# hotels}}",
|
||||
},
|
||||
{ amount: resultCount }
|
||||
)}
|
||||
</Preamble>
|
||||
<Typography variant="Title/Subtitle/md">
|
||||
<span>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
defaultMessage: "{amount, plural, one {# hotel} other {# hotels}}",
|
||||
},
|
||||
{ amount: resultCount }
|
||||
)}
|
||||
</span>
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import BookingCodeFilter from "@scandic-hotels/booking-flow/BookingCodeFilter"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import Link from "@scandic-hotels/design-system/Link"
|
||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import HotelCardListing from "@/components/HotelReservation/HotelCardListing"
|
||||
@@ -10,8 +8,8 @@ import HotelCount from "@/components/HotelReservation/SelectHotel/HotelCount"
|
||||
import HotelSorter from "@/components/HotelReservation/SelectHotel/HotelSorter"
|
||||
import MobileMapButtonContainer from "@/components/HotelReservation/SelectHotel/MobileMapButtonContainer"
|
||||
import NoAvailabilityAlert from "@/components/HotelReservation/SelectHotel/NoAvailabilityAlert"
|
||||
import { MapWithButtonWrapper } from "@/components/Maps/MapWithButtonWrapper"
|
||||
import StaticMap from "@/components/Maps/StaticMap"
|
||||
import { getIntl } from "@/i18n"
|
||||
|
||||
import { getFiltersFromHotels, type HotelResponse } from "./helpers"
|
||||
|
||||
@@ -38,8 +36,6 @@ export default async function SelectHotel({
|
||||
mapHref,
|
||||
title,
|
||||
}: SelectHotelProps) {
|
||||
const intl = await getIntl()
|
||||
|
||||
const isAllUnavailable = hotels.every(
|
||||
(hotel) => hotel.availability.status !== "Available"
|
||||
)
|
||||
@@ -63,7 +59,9 @@ export default async function SelectHotel({
|
||||
<div className={styles.headerContent}>
|
||||
<div className={styles.title}>
|
||||
<div className={styles.cityInformation}>
|
||||
<Subtitle>{title}</Subtitle>
|
||||
<Typography variant="Title/Subtitle/lg">
|
||||
<p>{title}</p>
|
||||
</Typography>
|
||||
<HotelCount />
|
||||
</div>
|
||||
<div className={styles.sorter}>
|
||||
@@ -78,7 +76,7 @@ export default async function SelectHotel({
|
||||
<div className={styles.sideBar}>
|
||||
{hotels.length ? (
|
||||
<Link className={styles.link} href={mapHref} keepSearchParams>
|
||||
<div className={styles.mapContainer}>
|
||||
<MapWithButtonWrapper>
|
||||
<StaticMap
|
||||
city={city.name}
|
||||
country={isCityWithCountry(city) ? city.country : undefined}
|
||||
@@ -88,15 +86,7 @@ export default async function SelectHotel({
|
||||
mapType="roadmap"
|
||||
altText={`Map of ${city.name} city center`}
|
||||
/>
|
||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||
<span className={styles.mapButton}>
|
||||
<MaterialIcon icon="map" color="CurrentColor" size={20} />
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "See on map",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
</div>
|
||||
</MapWithButtonWrapper>
|
||||
</Link>
|
||||
) : (
|
||||
<div className={styles.mapContainer}>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: var(--Spacing-x3) 0 var(--Spacing-x2);
|
||||
padding: var(--Space-x3) 0 var(--Space-x2);
|
||||
}
|
||||
|
||||
.headerContent {
|
||||
@@ -16,13 +16,13 @@
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x2);
|
||||
gap: var(--Space-x2);
|
||||
}
|
||||
|
||||
.cityInformation {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--Spacing-x1);
|
||||
gap: var(--Space-x1);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
@@ -43,21 +43,11 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
gap: var(--Spacing-x2);
|
||||
margin-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hotelList {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x3);
|
||||
gap: var(--Space-x3);
|
||||
}
|
||||
|
||||
.filter {
|
||||
@@ -65,25 +55,14 @@
|
||||
}
|
||||
|
||||
.skeletonContainer .title {
|
||||
margin-bottom: var(--Spacing-x3);
|
||||
}
|
||||
|
||||
.mapButton {
|
||||
display: flex;
|
||||
padding: 10px var(--Space-x2);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
background: var(--Component-Button-Inverted-Fill-Default);
|
||||
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, 0.1);
|
||||
gap: var(--Space-x05);
|
||||
margin-bottom: var(--Space-x3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main {
|
||||
padding: var(--Spacing-x5) 0;
|
||||
padding: var(--Space-x5) 0;
|
||||
flex-direction: row;
|
||||
gap: var(--Spacing-x5);
|
||||
gap: var(--Space-x5);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@@ -93,7 +72,7 @@
|
||||
|
||||
.header {
|
||||
background-color: var(--Base-Surface-Subtle-Normal);
|
||||
padding: var(--Spacing-x4) 0 var(--Spacing-x3);
|
||||
padding: var(--Space-x4) 0 var(--Space-x3);
|
||||
}
|
||||
|
||||
.sorter {
|
||||
@@ -126,35 +105,11 @@
|
||||
margin-bottom: var(--Space-x6);
|
||||
}
|
||||
|
||||
.mapContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--Base-Surface-Primary-light-Normal);
|
||||
border-radius: var(--Corner-radius-md);
|
||||
border: 1px solid var(--Base-Border-Subtle);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mapButton {
|
||||
position: absolute;
|
||||
bottom: var(--Space-x2);
|
||||
right: var(--Space-x2);
|
||||
}
|
||||
|
||||
.mapButton:hover {
|
||||
background-color: var(--Base-Button-Inverted-Fill-Hover);
|
||||
color: var(--Base-Button-Inverted-On-Fill-Hover);
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.skeletonContainer .title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.skeletonContainer .sideBar {
|
||||
gap: var(--Spacing-x3);
|
||||
gap: var(--Space-x3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"use client"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { FakeButton } from "@scandic-hotels/design-system/FakeButton"
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
|
||||
import styles from "./mapWithButtonWrapper.module.css"
|
||||
|
||||
export function MapWithButtonWrapper({ children }: React.PropsWithChildren) {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{children}
|
||||
<FakeButton
|
||||
variant="Primary"
|
||||
color="Inverted"
|
||||
size="Small"
|
||||
typography="Body/Supporting text (caption)/smBold"
|
||||
className={styles.button}
|
||||
>
|
||||
<MaterialIcon icon="map" color="CurrentColor" size={20} />
|
||||
{intl.formatMessage({
|
||||
defaultMessage: "See on map",
|
||||
})}
|
||||
</FakeButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
.container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
border-radius: var(--Corner-radius-md);
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
bottom: var(--Space-x2);
|
||||
right: var(--Space-x2);
|
||||
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
.fakeButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: var(--Corner-radius-rounded);
|
||||
gap: var(--Space-x05);
|
||||
}
|
||||
39
packages/design-system/lib/components/FakeButton/index.tsx
Normal file
39
packages/design-system/lib/components/FakeButton/index.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
'use client'
|
||||
|
||||
import { variants } from './variants'
|
||||
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { ComponentProps, PropsWithChildren } from 'react'
|
||||
import type { Button } from 'react-aria-components'
|
||||
|
||||
interface FakeButtonProps
|
||||
extends PropsWithChildren,
|
||||
Omit<ComponentProps<typeof Button>, 'children' | 'onPress'>,
|
||||
VariantProps<typeof variants> {}
|
||||
|
||||
export function FakeButton({
|
||||
variant,
|
||||
color,
|
||||
size,
|
||||
typography,
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: FakeButtonProps) {
|
||||
const classNames = variants({
|
||||
color,
|
||||
size,
|
||||
variant,
|
||||
typography,
|
||||
className,
|
||||
})
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames}
|
||||
{...(props as React.HTMLProps<HTMLSpanElement>)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import styles from './fakeButton.module.css'
|
||||
import { withButton } from '../Button'
|
||||
|
||||
export const variants = cva(styles.fakeButton, withButton({}))
|
||||
@@ -70,6 +70,5 @@ export default async function StaticMap({
|
||||
}
|
||||
|
||||
const src = getUrlWithSignature(url, googleMapSecret)
|
||||
|
||||
return <img src={src} alt={altText} />
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"./DeprecatedSelect": "./lib/components/DeprecatedSelect/index.tsx",
|
||||
"./Divider": "./lib/components/Divider/index.tsx",
|
||||
"./FacilityToIcon": "./lib/components/FacilityToIcon/index.tsx",
|
||||
"./FakeButton": "./lib/components/FakeButton/index.tsx",
|
||||
"./Footnote": "./lib/components/Footnote/index.tsx",
|
||||
"./Form/Checkbox": "./lib/components/Form/Checkbox/index.tsx",
|
||||
"./Form/Country": "./lib/components/Form/Country/index.tsx",
|
||||
|
||||
Reference in New Issue
Block a user