From 8738d8faf43423686057e26b6e91b7f4b0eec34f Mon Sep 17 00:00:00 2001
From: Maxime Sallerin <97036207+maxime-sallerin@users.noreply.github.com>
Date: Fri, 26 Jul 2024 09:58:54 +0200
Subject: [PATCH] Feature/dropdown (#179)
---
.../DropdownOption/classes.module.scss | 21 +++++++++
.../DropdownMenu/DropdownOption/index.tsx | 34 ++++++++++++++
.../Dropdown/DropdownMenu/classes.module.scss | 35 +++++++++++++++
.../Dropdown/DropdownMenu/index.tsx | 44 ++++++++++++++++++
.../DesignSystem/Dropdown/classes.module.scss | 45 +++++++++++++++++++
.../DesignSystem/Dropdown/index.tsx | 44 ++++++++++++++++++
.../DesignSystem/Typography/index.tsx | 3 ++
.../Layouts/DesignSystem/classes.module.scss | 1 +
.../Components/Layouts/DesignSystem/index.tsx | 31 ++++++++++---
9 files changed, 253 insertions(+), 5 deletions(-)
create mode 100644 src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/classes.module.scss
create mode 100644 src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/index.tsx
create mode 100644 src/front/Components/DesignSystem/Dropdown/DropdownMenu/classes.module.scss
create mode 100644 src/front/Components/DesignSystem/Dropdown/DropdownMenu/index.tsx
create mode 100644 src/front/Components/DesignSystem/Dropdown/classes.module.scss
create mode 100644 src/front/Components/DesignSystem/Dropdown/index.tsx
diff --git a/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/classes.module.scss b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/classes.module.scss
new file mode 100644
index 00000000..884a962d
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/classes.module.scss
@@ -0,0 +1,21 @@
+.root {
+ display: flex;
+ padding: var(--spacing-1, 8px) var(--spacing-2, 16px);
+ align-items: center;
+ gap: var(--spacing-sm, 8px);
+ justify-content: space-between;
+
+ border-radius: var(--dropdown-radius, 0px);
+ border: 1px solid var(--dropdown-border, rgba(0, 0, 0, 0));
+
+ background: var(--dropdown-option-background-default, #fff);
+
+ &:hover {
+ background-color: var(--dropdown-option-background-hovered);
+ }
+
+ &:focus,
+ &:active {
+ background-color: var(--dropdown-option-background-pressed);
+ }
+}
diff --git a/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/index.tsx b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/index.tsx
new file mode 100644
index 00000000..05be3eb9
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/DropdownOption/index.tsx
@@ -0,0 +1,34 @@
+import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
+import { CheckIcon } from "@heroicons/react/24/outline";
+import { useCallback } from "react";
+
+import classes from "./classes.module.scss";
+import IconButton from "@Front/Components/DesignSystem/IconButton";
+
+export type IOption = {
+ id: string;
+ label: string;
+};
+
+type IProps = {
+ option: IOption;
+ isActive: boolean;
+ onClick?: (option: IOption) => void;
+};
+
+export default function DropdownOption(props: IProps) {
+ const { option, onClick, isActive } = props;
+
+ const handleOnClick = useCallback(() => onClick && onClick(option), [onClick, option]);
+
+ return (
+
+
+ {option.label}
+
+ {isActive && } />}
+
+ );
+}
diff --git a/src/front/Components/DesignSystem/Dropdown/DropdownMenu/classes.module.scss b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/classes.module.scss
new file mode 100644
index 00000000..19135403
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/classes.module.scss
@@ -0,0 +1,35 @@
+.root {
+ position: relative;
+ overflow: hidden;
+
+ .content {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ padding: var(--spacing-sm, 8px);
+ border-radius: var(--dropdown-radius, 0px);
+
+ background: var(--dropdown-menu-background, #fff);
+
+ border: 1px solid var(--dropdown-menu-border-primary, #005bcb);
+
+ max-height: 0;
+ opacity: 0;
+ transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
+
+ position: absolute;
+ top: 100%;
+ left: 0;
+ transform: translateY(8px);
+ }
+
+ &.open {
+ overflow: visible;
+ .content {
+ max-height: 500px;
+ opacity: 1;
+ }
+ }
+}
diff --git a/src/front/Components/DesignSystem/Dropdown/DropdownMenu/index.tsx b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/index.tsx
new file mode 100644
index 00000000..c19f6982
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/DropdownMenu/index.tsx
@@ -0,0 +1,44 @@
+import classNames from "classnames";
+import React, { useCallback } from "react";
+
+import classes from "./classes.module.scss";
+import DropdownOption, { IOption } from "./DropdownOption";
+
+type IProps = {
+ options: IOption[];
+ selectedOption: IOption | null;
+ children: React.ReactNode;
+ openable: {
+ isOpen: boolean;
+ open: () => void;
+ close: () => void;
+ toggle: () => void;
+ };
+ onSelect?: (option: IOption) => void;
+};
+export default function DropdownMenu(props: IProps) {
+ const { children, options, onSelect, openable, selectedOption } = props;
+
+ const handleSelect = useCallback(
+ (option: IOption) => {
+ onSelect?.(option);
+ openable.close();
+ },
+ [onSelect, openable],
+ );
+
+ return (
+
+ {children}
+
+ {options.map((option) => {
+ return ;
+ })}
+
+
+ );
+
+ function isActive(option: IOption): boolean {
+ return selectedOption?.id === option.id;
+ }
+}
diff --git a/src/front/Components/DesignSystem/Dropdown/classes.module.scss b/src/front/Components/DesignSystem/Dropdown/classes.module.scss
new file mode 100644
index 00000000..8406a67c
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/classes.module.scss
@@ -0,0 +1,45 @@
+@import "@Themes/constants.scss";
+
+.root {
+ cursor: pointer;
+ height: 56px;
+
+ display: flex;
+ align-items: center;
+
+ padding: var(--spacing-2, 16px) var(--spacing-sm, 8px);
+ gap: var(--spacing-2, 16px);
+
+ border-radius: var(--input-radius, 0px);
+ border: 1px solid var(--dropdown-input-border-default, #d7dce0);
+ background: var(--dropdown-input-background, #fff);
+
+ .content {
+ width: 100%;
+ display: flex;
+ padding: 0px var(--spacing-2, 16px);
+ align-items: center;
+ flex: 1 0 0;
+ }
+
+ &:hover {
+ border-color: var(--dropdown-input-border-hovered);
+ }
+
+ &.active {
+ border-color: var(--dropdown-input-border-filled);
+ }
+
+ &.open {
+ border-color: var(--dropdown-input-border-expanded);
+
+ svg {
+ transform: rotate(180deg);
+ }
+ }
+
+ &.disabled {
+ opacity: var(--opacity-disabled, 0.3);
+ pointer-events: none;
+ }
+}
diff --git a/src/front/Components/DesignSystem/Dropdown/index.tsx b/src/front/Components/DesignSystem/Dropdown/index.tsx
new file mode 100644
index 00000000..a819e349
--- /dev/null
+++ b/src/front/Components/DesignSystem/Dropdown/index.tsx
@@ -0,0 +1,44 @@
+import useOpenable from "@Front/Hooks/useOpenable";
+import classNames from "classnames";
+import { useState } from "react";
+
+import Typography, { ETypo, ETypoColor } from "../Typography";
+import classes from "./classes.module.scss";
+import DropdownMenu from "./DropdownMenu";
+import { IOption } from "./DropdownMenu/DropdownOption";
+import IconButton from "../IconButton";
+import { ChevronDownIcon } from "@heroicons/react/24/outline";
+
+type IProps = {
+ options: IOption[];
+ placeholder: string;
+ disabled?: boolean;
+};
+
+export default function Dropdown(props: IProps) {
+ const { options, placeholder, disabled } = props;
+ const [selectedOption, setSelectedOption] = useState(null);
+ const openable = useOpenable({ defaultOpen: false });
+
+ return (
+
+
+
+
+ {selectedOption?.label ?? placeholder}
+
+
+
} />
+
+
+ );
+}
diff --git a/src/front/Components/DesignSystem/Typography/index.tsx b/src/front/Components/DesignSystem/Typography/index.tsx
index 2aaeec59..9f63599c 100644
--- a/src/front/Components/DesignSystem/Typography/index.tsx
+++ b/src/front/Components/DesignSystem/Typography/index.tsx
@@ -156,6 +156,9 @@ export enum ETypoColor {
CONTRAST_ACTIVED = "--contrast-actived",
NAVIGATION_BUTTON_CONTRAST_DEFAULT = "--navigation-button-contrast-default",
NAVIGATION_BUTTON_CONTRAST_ACTIVE = "--navigation-button-contrast-active",
+
+ DROPDOWN_CONTRAST_DEFAULT = "--dropdown-contrast-default",
+ DROPDOWN_CONTRAST_ACTIVE = "--dropdown-contrast-active",
}
export default function Typography(props: IProps) {
diff --git a/src/front/Components/Layouts/DesignSystem/classes.module.scss b/src/front/Components/Layouts/DesignSystem/classes.module.scss
index 57f1d643..90be8834 100644
--- a/src/front/Components/Layouts/DesignSystem/classes.module.scss
+++ b/src/front/Components/Layouts/DesignSystem/classes.module.scss
@@ -3,6 +3,7 @@
flex-direction: column;
gap: 32px;
.components {
+ max-width: 600px;
.inputs {
display: flex;
flex-direction: column;
diff --git a/src/front/Components/Layouts/DesignSystem/index.tsx b/src/front/Components/Layouts/DesignSystem/index.tsx
index 276577a6..708e58a8 100644
--- a/src/front/Components/Layouts/DesignSystem/index.tsx
+++ b/src/front/Components/Layouts/DesignSystem/index.tsx
@@ -1,12 +1,17 @@
import Alert, { EAlertVariant } from "@Front/Components/DesignSystem/Alert";
import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
+import Dropdown from "@Front/Components/DesignSystem/Dropdown";
+import Footer from "@Front/Components/DesignSystem/Footer";
import Form from "@Front/Components/DesignSystem/Form";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
+import Menu from "@Front/Components/DesignSystem/Menu";
import Modal from "@Front/Components/DesignSystem/Modal";
import Newsletter from "@Front/Components/DesignSystem/Newsletter";
+import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
+import DropdownNavigation from "@Front/Components/DesignSystem/SearchBlockList/DropdownNavigation";
import Table from "@Front/Components/DesignSystem/Table";
import Tag, { ETagColor, ETagVariant } from "@Front/Components/DesignSystem/Tag";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
@@ -14,7 +19,6 @@ import NumberPicker from "@Front/Components/Elements/NumberPicker";
import Tabs from "@Front/Components/Elements/Tabs";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
import useOpenable from "@Front/Hooks/useOpenable";
-import Footer from "@Front/Components/DesignSystem/Footer";
import {
ArchiveBoxIcon,
ArrowLongLeftIcon,
@@ -27,9 +31,6 @@ import {
import { useCallback, useMemo, useState } from "react";
import classes from "./classes.module.scss";
-import Menu from "@Front/Components/DesignSystem/Menu";
-import DropdownNavigation from "@Front/Components/DesignSystem/SearchBlockList/DropdownNavigation";
-import SearchBlockList from "@Front/Components/DesignSystem/SearchBlockList";
export default function DesignSystem() {
const { isOpen, open, close } = useOpenable();
@@ -78,8 +79,28 @@ export default function DesignSystem() {
return (
DesignSystem
-
+
+
+ Dropdown
+
+
Navigation latérale