diff --git a/base.scss b/base.scss index e89fb9608..5ada000ec 100644 --- a/base.scss +++ b/base.scss @@ -22,8 +22,8 @@ @import "./src/stories/Library/Forms/checkbox/checkbox"; @import "./src/stories/Library/cover/cover"; @import "./src/stories/Library/Forms/input/input"; -@import "./src/stories/Library/Forms/date-picker/date-picker.scss"; -@import "./src/stories/Library/date-calendar/date-calendar.scss"; +@import "./src/stories/Library/Forms/date-picker/date-picker"; +@import "./src/stories/Library/date-calendar/date-calendar"; @import "./src/stories/Library/list-buttons/list-buttons"; @import "./src/stories/Library/progress-bar/progress-bar"; @import "./src/stories/Library/missing-story/missing-story"; @@ -128,6 +128,7 @@ @import "./src/stories/Blocks/article/article"; @import "./src/stories/Blocks/event-page/event-page"; @import "./src/stories/Blocks/event-list-page/event-list-page"; +@import "./src/stories/Blocks/reservation-page/reservation-page-skeleton"; @import "./src/styles/scss/shared"; @import "./src/styles/scss/internal"; diff --git a/src/stories/Blocks/fee-list/FeeList.stories.tsx b/src/stories/Blocks/fee-list/FeeList.stories.tsx new file mode 100644 index 000000000..7500fbd19 --- /dev/null +++ b/src/stories/Blocks/fee-list/FeeList.stories.tsx @@ -0,0 +1,13 @@ +import { ComponentMeta, ComponentStory } from "@storybook/react"; +import FeeList from "./FeeList"; + +export default { + title: "Blocks / Fee List", + component: FeeList, +} as ComponentMeta; + +const Template: ComponentStory = (args) => { + return ; +}; + +export const Item = Template.bind({}); diff --git a/src/stories/Blocks/fee-list/FeeList.tsx b/src/stories/Blocks/fee-list/FeeList.tsx new file mode 100644 index 000000000..ae69261bc --- /dev/null +++ b/src/stories/Blocks/fee-list/FeeList.tsx @@ -0,0 +1,664 @@ +// This is copy/pasted from dpl-react because no design story was made for this view. + +// TODO: Should be converted to react components. +const FeeList: React.FC = () => { + return ( +
+

+ Gebyrer & erstatninger +

+
+
+ Overdue fees and replacement costs that were created before 27/10/2020 + can still be paid on this page. +
+ +
+
+ + Go to payment page + + +
+
+
+
+

+ Betales på biblioteket + 7 +

+
+ + + + + + + +
+
+

+ Har du allerede betalt? Der kan gå op til 72 timer før + betalingen registreres i vores systemer +

+

I alt: 137.75

+
+
+
+
+
+
+
+

+ Betales på Mit betalingsoverblik + 6 +

+
+ + + + + + +
+
 
+
+

+ Har du allerede betalt? Der kan gå op til 72 timer før + betalingen registreres i vores systemer +

+

I alt: 146

+
+
+
+
+
+ ); +}; + +export default FeeList; diff --git a/src/stories/Blocks/reservation-page/ReservationListEmptyState.tsx b/src/stories/Blocks/reservation-page/ReservationListEmptyState.tsx new file mode 100644 index 000000000..9992ca969 --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationListEmptyState.tsx @@ -0,0 +1,13 @@ +export interface ReservationListEmptyStateProps { + text: string; + classsNames?: string; +} + +const ReservationListEmptyState: React.FC = ({ + text, + classsNames, +}) => { + return
{text}
; +}; + +export default ReservationListEmptyState; diff --git a/src/stories/Blocks/reservation-page/ReservationListItem.tsx b/src/stories/Blocks/reservation-page/ReservationListItem.tsx new file mode 100644 index 000000000..753b1cc3e --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationListItem.tsx @@ -0,0 +1,82 @@ +import { Counter } from "../../Library/counter/Counter"; +import { ReactComponent as ArrowSmallRight } from "../../Library/Arrows/icon-arrow-ui/icon-arrow-ui-small-right.svg"; + +export interface ReservationListItemProps { + amount: number; +} + +const ReservationListItem: React.FC = ({ + amount, +}) => { + const listItems = Array(amount).fill(0); + return ( + <> + {listItems.map(() => ( +
+
+
+
+
+
+
+
bog
+
+
+ +

+ Jørn Lier Horst (2020) +

+

+ Detektivbureau Nr. 2 +

+
+
+
+
+
+
+ +
+
+
+
+ Pick up before 02-02-2024 +
+

Hovedbiblioteket

+

Reserveringshylde 74

+
+
+ +
+
+ ))} + + ); +}; + +export default ReservationListItem; diff --git a/src/stories/Blocks/reservation-page/ReservationListItemSkeleton.tsx b/src/stories/Blocks/reservation-page/ReservationListItemSkeleton.tsx new file mode 100644 index 000000000..4b027136d --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationListItemSkeleton.tsx @@ -0,0 +1,29 @@ +const ReservationListItemSkeleton = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export default ReservationListItemSkeleton; diff --git a/src/stories/Blocks/reservation-page/ReservationPage.stories.tsx b/src/stories/Blocks/reservation-page/ReservationPage.stories.tsx new file mode 100644 index 000000000..3081f3326 --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationPage.stories.tsx @@ -0,0 +1,66 @@ +import { ComponentStory } from "@storybook/react"; +import { withDesign } from "storybook-addon-designs"; +import ReservationPage, { ReservationPageProps } from "./ReservationPage"; + +export default { + title: "Blocks / Reservation / Reservation Page", + component: ReservationPage, + decorators: [withDesign], + argTypes: { + headline: { + name: "Headline", + defaultValue: "Your reservations", + control: { type: "text" }, + }, + readyForPickup: { + name: "Ready for pickup amount", + defaultValue: 2, + control: { type: "number" }, + }, + physicalReservations: { + name: "Physical reservations amount", + defaultValue: 2, + control: { type: "number" }, + }, + digitalReservations: { + name: "Digital reservations amount", + defaultValue: 2, + control: { type: "number" }, + }, + skeletonVersion: { + name: "Is skeleton version?", + defaultValue: false, + control: { type: "boolean" }, + }, + }, + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/file/Zx9GrkFA3l4ISvyZD2q0Qi/Designsystem?type=design&node-id=8513%3A85811&mode=design&t=nde0qiy8WvxnX8im-1", + }, + }, +}; + +const Template: ComponentStory = ( + args: ReservationPageProps +) => ; + +export const Default = Template.bind({}); +export const NoReadyForPickup = Template.bind({}); +NoReadyForPickup.args = { + readyForPickup: 0, +}; +export const NoPhysicalReservations = Template.bind({}); +NoPhysicalReservations.args = { + physicalReservations: 0, +}; +export const NoReservationsAtAll = Template.bind({}); +NoReservationsAtAll.args = { + readyForPickup: 0, + physicalReservations: 0, + digitalReservations: 0, +}; +export const SkeletonVersion = Template.bind({}); +SkeletonVersion.args = { + skeletonVersion: true, +}; diff --git a/src/stories/Blocks/reservation-page/ReservationPage.tsx b/src/stories/Blocks/reservation-page/ReservationPage.tsx new file mode 100644 index 000000000..f7b521b10 --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationPage.tsx @@ -0,0 +1,136 @@ +import ResultPager from "../../Library/card-list-page/ResultPager"; +import { PauseReservation } from "../../Library/pause-reservation/PauseReservation"; +import ReservationListEmptyState from "./ReservationListEmptyState"; +import ReservationListItem from "./ReservationListItem"; +import ReservationPageSkeleton from "./ReservationPageSkeleton"; + +export interface ReservationPageProps { + headline: string; + readyForPickup: number; + physicalReservations: number; + digitalReservations: number; + skeletonVersion?: boolean; +} + +const ReservationPage: React.FC = ({ + headline, + readyForPickup, + physicalReservations, + digitalReservations, + skeletonVersion, +}) => { + if (skeletonVersion) { + return ; + } + + if (!readyForPickup && !physicalReservations && !digitalReservations) { + return ( +
+

{headline}

+ +
+ ); + } + + return ( +
+

{headline}

+ + +
+
+

+ Ready for pickup +
{readyForPickup}
+

+
+ {!!readyForPickup && ( +
+
    +
  • + +
  • +
+ +
+ )} + {!readyForPickup && ( + + )} +
+ +
+
+

+ Physical reservations +
+ {physicalReservations} +
+

+
+ {!!physicalReservations && ( +
+
    +
  • + +
  • +
+ +
+ )} + {!physicalReservations && ( + + )} +
+ +
+
+

+ Digital reservations +
{digitalReservations}
+

+
+ {!!digitalReservations && ( +
+
    +
  • + +
  • +
+ +
+ )} + {!digitalReservations && ( + + )} +
+
+ ); +}; + +export default ReservationPage; diff --git a/src/stories/Blocks/reservation-page/ReservationPageSkeleton.tsx b/src/stories/Blocks/reservation-page/ReservationPageSkeleton.tsx new file mode 100644 index 000000000..89a108d19 --- /dev/null +++ b/src/stories/Blocks/reservation-page/ReservationPageSkeleton.tsx @@ -0,0 +1,21 @@ +import ReservationListItemSkeleton from "./ReservationListItemSkeleton"; + +const ReservationPageSkeleton: React.FC = () => { + return ( +
+
+
+
+ + +
+ + +
+ + +
+ ); +}; + +export default ReservationPageSkeleton; diff --git a/src/stories/Blocks/reservation-page/reservation-page-skeleton.scss b/src/stories/Blocks/reservation-page/reservation-page-skeleton.scss new file mode 100644 index 000000000..4aba799da --- /dev/null +++ b/src/stories/Blocks/reservation-page/reservation-page-skeleton.scss @@ -0,0 +1,23 @@ +// Since we are using the Skeleton Screen Css classes connected to the existing styling +// we deliberately not follow the BEM naming convention here. +.reservation-list-page.ssc { + .ssc-square { + height: 72px; + + &.cover--size-small { + height: 137px; + width: 95px; + } + } + .ssc-circle { + height: 90px; + } + + & .list-reservation__information { + min-width: 200px; + + @include media-query__x-small("max-width") { + min-width: 100px; + } + } +} diff --git a/src/stories/Library/Arrows/arrows.scss b/src/stories/Library/Arrows/arrows.scss index f4cf15c97..766efb504 100644 --- a/src/stories/Library/Arrows/arrows.scss +++ b/src/stories/Library/Arrows/arrows.scss @@ -45,4 +45,11 @@ // This width mimics what the expanded svg width. The class is intended to be // used when the arrow needs to be within a button so they look the same. width: 101px; + + &:focus { + svg { + visibility: visible; + opacity: 1; + } + } } diff --git a/src/stories/Library/Lists/fee-list/fee-list.scss b/src/stories/Library/Lists/fee-list/fee-list.scss index b889d4c94..0af838b02 100644 --- a/src/stories/Library/Lists/fee-list/fee-list.scss +++ b/src/stories/Library/Lists/fee-list/fee-list.scss @@ -3,11 +3,15 @@ padding: $s-md; position: relative; z-index: 0; + @include typography($typo__body-placeholder); @include media-query__medium { padding: $s-6xl; } } +.fee-list { + margin-top: $s-lg; +} .fee-list-bottom { display: flex; @@ -22,3 +26,20 @@ .fee-list-bottom__actions > button { margin-left: auto; } + +.fee-list-body__payment-button { + margin: 18px 0px; + display: flex; + + justify-content: center; + align-items: center; + + @include media-query__medium { + flex-direction: row-reverse; + justify-content: right; + } +} + +.fee-list-body__payment-info-link { + margin-top: 18px; +} diff --git a/src/stories/Library/Lists/list-materials/list-materials.scss b/src/stories/Library/Lists/list-materials/list-materials.scss index 3958c25d9..1c5837977 100644 --- a/src/stories/Library/Lists/list-materials/list-materials.scss +++ b/src/stories/Library/Lists/list-materials/list-materials.scss @@ -1,5 +1,7 @@ .list-materials { @include show-svg-on-hover(".list-dashboard__arrow"); + @include show-svg-on-hover(".list-materials__arrow"); + @include show-svg-on-hover(".arrow-button"); display: flex; min-height: 96px; @@ -33,6 +35,8 @@ } .list-materials--disabled { + @include show-svg-on-hover(".list-materials__arrow"); + .checkbox { .checkbox__icon { opacity: 0; diff --git a/src/stories/Library/Modals/modal-details/modal-details.scss b/src/stories/Library/Modals/modal-details/modal-details.scss index e3b0bf103..8fe52ac75 100644 --- a/src/stories/Library/Modals/modal-details/modal-details.scss +++ b/src/stories/Library/Modals/modal-details/modal-details.scss @@ -12,7 +12,6 @@ $MODAL_DETAILS_CONTAINER_WIDTH: 1000px; .modal-details__container { width: 100%; - overflow: auto; max-width: 100%; padding-bottom: $s-2xl; diff --git a/src/stories/Library/Modals/modal-pause/ModalPause.tsx b/src/stories/Library/Modals/modal-pause/ModalPause.tsx index aaa9956a0..5d07ca699 100644 --- a/src/stories/Library/Modals/modal-pause/ModalPause.tsx +++ b/src/stories/Library/Modals/modal-pause/ModalPause.tsx @@ -1,5 +1,8 @@ -import React from "react"; +import React, { MutableRefObject, useCallback, useRef } from "react"; +import { BaseOptions } from "flatpickr/dist/types/options"; +import { Instance } from "flatpickr/dist/types/instance"; +import flatpickr from "flatpickr"; import { Button } from "../../Buttons/button/Button"; import { Links } from "../../links/Links"; import Modal from "../Modal"; @@ -18,59 +21,81 @@ export const ModalPause: React.FC = ({ subtitle, textWithLink, linkText, -}) => ( - -
-

{title}

-
-

{subtitle}

-
+}) => { + const picker = useRef() as MutableRefObject; -
-
-
- - -
+ const calendar = useCallback((node: Node | null) => { + const options: Partial = { + mode: "range", + // defaultDate: ["2024-01-01", "2024-01-10"], + now: "2024-01-19", + altInput: true, + dateFormat: "d-m-Y", + static: true, + onReady: (selectedDates, dateStr, instance) => { + instance.altInput?.setAttribute("aria-label", "Pause period"); + const classes = + instance.altInput?.getAttribute("class")?.split(" ") || []; + + // Set placeholder if no dates are chosen and a placeholder is given. + if (!selectedDates.length) { + instance.altInput?.setAttribute("placeholder", "Choose pause period"); + } + // Set empty-date-range class if no dates are chosen. + if (!selectedDates.length && !classes.includes("empty-date-range")) { + classes.push("empty-date-range"); + instance.altInput?.setAttribute("class", classes.join(" ")); + } + }, + }; + + if (node !== null) { + picker.current = flatpickr(node, options); + } + }, []); + + return ( + +
+

{title}

+
+

{subtitle}

+
-
- - +
+
+
+ + +
-
-
+
+

{textWithLink}

+

- {textWithLink} +

+
+
-
-
-
-
-); + + ); +}; diff --git a/src/stories/Library/Modals/modal-pause/modal-pause.scss b/src/stories/Library/Modals/modal-pause/modal-pause.scss index cd63d2b31..dcf00d04f 100644 --- a/src/stories/Library/Modals/modal-pause/modal-pause.scss +++ b/src/stories/Library/Modals/modal-pause/modal-pause.scss @@ -1,14 +1,26 @@ -$modal-pause-container-width: 550px; - .modal-pause { flex-direction: column; align-items: center; + + &__subtitle { + margin-top: $s-xl; + color: $color__text-secondary-gray; + } + + &__date-range { + margin-top: $s-lg; + } + + &__text-link { + color: $color__text-secondary-gray; + margin-bottom: $s-xs; + } } .modal-pause__container { margin: 0 $s-lg; padding-top: 96px; - max-width: $modal-pause-container-width; + max-width: 550px; width: 100%; @include media-query__small { @@ -21,12 +33,7 @@ $modal-pause-container-width: 550px; justify-content: center; } -.datepickers { - @include media-query__small { - display: flex; - justify-content: space-between; - } - +.date-range { input { border: 1px solid $color__global-tertiary-1; box-sizing: border-box; @@ -36,27 +43,12 @@ $modal-pause-container-width: 550px; padding: 0 10px; color: $color__text-secondary-gray; @include typography($typo__button); - - @include media-query__small { - min-width: 250px; - padding: 0 20px; - } } - .datepicker { + .date-range__input { display: flex; flex-direction: column; width: 100%; margin-bottom: $s-lg; - - @include media-query__small { - margin-bottom: 0; - } - - &:first-of-type { - @include media-query__small { - margin-right: $s-lg; - } - } } } diff --git a/src/stories/Library/card-list-page/ResultPager.tsx b/src/stories/Library/card-list-page/ResultPager.tsx index b9f0a49e1..3196a6079 100644 --- a/src/stories/Library/card-list-page/ResultPager.tsx +++ b/src/stories/Library/card-list-page/ResultPager.tsx @@ -15,16 +15,18 @@ const ResultPager = ({ return (

- {`Viser ${currentResults} ud af ${total} resultater`} + {`Showing ${currentResults} out of ${total} results`}

-
); }; diff --git a/src/stories/Library/date-calendar/date-calendar.scss b/src/stories/Library/date-calendar/date-calendar.scss index cf9e6708c..ae2fcbb0f 100644 --- a/src/stories/Library/date-calendar/date-calendar.scss +++ b/src/stories/Library/date-calendar/date-calendar.scss @@ -9,7 +9,7 @@ $hover-background: rgba(0, 0, 0, 0.05); $selected-date-background: rgb(0, 105, 255); .flatpickr-calendar { - font-family: "Noto Sans JP"; + font-family: "Noto Sans"; font-style: normal; font-weight: 500; color: $color__text-primary-black; diff --git a/src/stories/Library/pause-reservation/PauseReservation.tsx b/src/stories/Library/pause-reservation/PauseReservation.tsx index 4807fe757..7b9be8cbc 100644 --- a/src/stories/Library/pause-reservation/PauseReservation.tsx +++ b/src/stories/Library/pause-reservation/PauseReservation.tsx @@ -1,8 +1,9 @@ export type PauseReservationProps = { - isChecked: boolean; + isChecked?: boolean; isPausedtext: string; pauseText: string; - dates: string; + dates?: string; + classNames?: string; }; export const PauseReservation = ({ @@ -10,9 +11,10 @@ export const PauseReservation = ({ pauseText, isPausedtext, dates, + classNames, }: PauseReservationProps) => { return ( -
+
@@ -21,7 +23,7 @@ export const PauseReservation = ({
{isChecked ? isPausedtext : pauseText}
- {isChecked && ( + {isChecked && dates && (
diff --git a/src/styles/scss/skeleton.scss b/src/styles/scss/skeleton.scss index 785043676..be2a7597f 100644 --- a/src/styles/scss/skeleton.scss +++ b/src/styles/scss/skeleton.scss @@ -1,3 +1,6 @@ $skeleton-line-border-radius: 3px; -@import "../../../node_modules/skeleton-screen-css/dist/index"; +// Es lint in CI is complaing and something else is complaining locally. +// I choose to shut up es lint in CI. +// stylelint-disable-next-line scss/at-import-partial-extension +@import "../../../node_modules/skeleton-screen-css/dist/index.scss";