From 664823d7954cc301431849a661e2b01333081cb9 Mon Sep 17 00:00:00 2001 From: DerekAgility Date: Fri, 8 May 2026 19:10:13 -0400 Subject: [PATCH] Select with caption and icons, as well as tooltip if the value is too long --- package.json | 2 +- .../atoms/Typography/Paragraph/Paragraph.tsx | 6 +-- .../inputs/select/Select.stories.tsx | 10 +++- stories/molecules/inputs/select/Select.tsx | 53 +++++++++++++++---- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index fea6392..03cbbfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agility/plenum-ui", - "version": "2.3.2", + "version": "2.3.3", "license": "MIT", "main": "dist/index.js", "module": "dist/index.js", diff --git a/stories/atoms/Typography/Paragraph/Paragraph.tsx b/stories/atoms/Typography/Paragraph/Paragraph.tsx index c3014ce..4d747c9 100644 --- a/stories/atoms/Typography/Paragraph/Paragraph.tsx +++ b/stories/atoms/Typography/Paragraph/Paragraph.tsx @@ -3,7 +3,7 @@ import { default as cn } from "classnames"; type ParagraphAs = "span" | "p" | "label" | "strong" | "em"; type ParagraphSize = "xl" | "lg" | "md" | "sm" | "xs"; -export interface ParagraphProps { +export interface ParagraphProps extends React.HTMLAttributes { as?: ParagraphAs; size?: ParagraphSize; children: React.ReactNode; @@ -18,8 +18,8 @@ const paragraphStyles: Record = { xs: "text-[10px] leading-[12px]" }; -export default function Paragraph({ as = "p", size = "md", children, className }: ParagraphProps) { +export default function Paragraph({ as = "p", size = "md", children, className, ...rest }: ParagraphProps) { const Tag = as; - return {children}; + return {children}; } diff --git a/stories/molecules/inputs/select/Select.stories.tsx b/stories/molecules/inputs/select/Select.stories.tsx index f0a01fd..45f4d45 100644 --- a/stories/molecules/inputs/select/Select.stories.tsx +++ b/stories/molecules/inputs/select/Select.stories.tsx @@ -61,8 +61,14 @@ export const DefaultSelect: TStory = { id: "select", name: "select", options: [ - { label: "Canada", value: "value1", description: "A description for Canada." }, - { label: "USA", value: "value2" } + { + label: "All", + value: "" + }, + { + label: "Canadian French blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah", + value: "fr-ca" + } ], isDisabled: false, isError: false, diff --git a/stories/molecules/inputs/select/Select.tsx b/stories/molecules/inputs/select/Select.tsx index 2047d89..565a55d 100644 --- a/stories/molecules/inputs/select/Select.tsx +++ b/stories/molecules/inputs/select/Select.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useLayoutEffect, useRef, useState } from "react"; import InputLabel from "@/stories/molecules/inputs/InputLabel"; -import { DynamicIcon } from "@/stories/atoms/icons/DynamicIcon"; +import { DynamicIcon, UnifiedIconName } from "@/stories/atoms/icons/DynamicIcon"; import { useId } from "@/utils/useId"; import { default as cn } from "classnames"; import { @@ -15,8 +15,9 @@ import { Paragraph } from "@/stories/atoms/Typography/Paragraph"; export interface ISimpleSelectOptions { label: string; value: string; - emoji?: string; + icon?: UnifiedIconName; description?: string; + caption?: string; } export interface ISelectProps { @@ -44,6 +45,7 @@ export interface ISelectProps { inputRef?: React.RefObject; placeholder?: string; dropdownMaxHeight?: number; + dropdownMaxWidth?: number; } const Select: React.FC = ({ @@ -62,7 +64,8 @@ const Select: React.FC = ({ message, inputRef, placeholder = "Select", - dropdownMaxHeight = 240 + dropdownMaxHeight = 240, + dropdownMaxWidth = 240 }) => { const uniqueID = useId(); if (!id) id = `select-${uniqueID}`; @@ -142,13 +145,15 @@ const Select: React.FC = ({ style={ { "--anchor-max-height": `${dropdownMaxHeight}px`, - minWidth: containerWidth + "--dropdown-max-width": `${dropdownMaxWidth}px`, + minWidth: Math.max(containerWidth ?? 0, 60) } as React.CSSProperties } className={cn( "z-[9999] overflow-auto rounded bg-white py-1", "text-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none", - "[--anchor-gap:8px]" + "[--anchor-gap:8px]", + "![max-width:var(--dropdown-max-width)]" )} > {options.map((option) => ( @@ -163,10 +168,40 @@ const Select: React.FC = ({ } > {({ selected }) => ( -
- {option.label} - {option.description ? ( - {option.description} +
+
+
+ { + const el = e.currentTarget; + if (el.scrollWidth <= el.clientWidth) el.removeAttribute("title"); + }} + onMouseLeave={(e) => { + e.currentTarget.setAttribute("title", option.label); + }} + > + {option.label} + + {option.description ? ( + + {option.description} + + ) : null} +
+ {option.caption ? ( + + {option.caption} + + ) : null} +
+ {option.icon ? ( + ) : null}
)}