From 467110641e9ca6f6097966225425158b082246a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:40:34 +0100 Subject: [PATCH 1/6] Several updates & fixes --- .../screens/common/themes/advanced-theme.json | 24 +- .../common/themes/opinionated-theme.json | 2 +- .../accordion/code/AccordionCodePage.tsx | 4 +- .../checkbox/code/CheckboxCodePage.tsx | 2 +- .../dialog/specs/DialogSpecsPage.tsx | 50 --- .../header/specs/HeaderSpecsPage.tsx | 16 +- .../progress-bar/code/ProgressBarCodePage.tsx | 2 +- .../progress-bar/code/examples/overlay.ts | 3 +- .../components/switch/code/SwitchCodePage.tsx | 8 +- .../components/toast/code/ToastCodePage.tsx | 8 + .../components/wizard/code/WizardCodePage.tsx | 8 +- .../localization/LocalizationPage.tsx | 2 +- .../screens/principles/themes/ThemesPage.tsx | 8 +- .../principles/themes/examples/bloomTheme.ts | 2 +- .../themes/schemas/advanced.schema.json | 24 +- .../themes/schemas/opinionated.schema.json | 2 +- packages/lib/src/HalstackContext.tsx | 8 +- packages/lib/src/accordion/Accordion.tsx | 124 +++--- packages/lib/src/alert/types.ts | 56 +++ packages/lib/src/common/variables.ts | 28 +- packages/lib/src/dialog/Dialog.tsx | 67 +-- packages/lib/src/file-input/FileInput.tsx | 268 ++++++------ packages/lib/src/file-input/FileItem.tsx | 106 ++--- packages/lib/src/file-input/utils.ts | 27 ++ packages/lib/src/header/Header.stories.tsx | 2 +- packages/lib/src/header/Header.tsx | 22 +- packages/lib/src/icon/Icon.tsx | 1 - packages/lib/src/progress-bar/ProgressBar.tsx | 113 +++-- packages/lib/src/slider/Slider.tsx | 291 ++++++------- packages/lib/src/switch/Switch.tsx | 4 +- packages/lib/src/text-input/Suggestion.tsx | 76 ++-- packages/lib/src/text-input/Suggestions.tsx | 92 ++-- packages/lib/src/text-input/TextInput.tsx | 407 ++++++++---------- packages/lib/src/text-input/utils.ts | 64 +++ packages/lib/src/toast/types.ts | 35 +- packages/lib/src/wizard/Icons.tsx | 39 ++ packages/lib/src/wizard/Wizard.tsx | 196 ++++----- 37 files changed, 1094 insertions(+), 1097 deletions(-) create mode 100644 packages/lib/src/file-input/utils.ts create mode 100644 packages/lib/src/text-input/utils.ts create mode 100644 packages/lib/src/wizard/Icons.tsx diff --git a/apps/website/screens/common/themes/advanced-theme.json b/apps/website/screens/common/themes/advanced-theme.json index 0a2cbdc4aa..982aee2737 100644 --- a/apps/website/screens/common/themes/advanced-theme.json +++ b/apps/website/screens/common/themes/advanced-theme.json @@ -404,14 +404,8 @@ "overlayColor": "#000000b3", "backgroundColor": "#ffffff", "closeIconSize": "24px", - "closeIconTopPosition": "20px", - "closeIconRightPosition": "20px", "closeIconBackgroundColor": "transparent", - "closeIconBorderColor": "none", "closeIconColor": "#000000", - "closeIconBorderThickness": "0px", - "closeIconBorderStyle": "solid", - "closeIconBorderRadius": "2px", "boxShadowOffsetX": "0px", "boxShadowOffsetY": "1px", "boxShadowBlur": "3px", @@ -546,15 +540,15 @@ }, "header": { "backgroundColor": "#ffffff", - "hamburguerFocusColor": "#0095ff", - "hamburguerFontFamily": "Open Sans, sans-serif", - "hamburguerFontStyle": "normal", - "hamburguerFontColor": "#000000", - "hamburguerFontSize": "10px", - "hamburguerFontWeight": "600", - "hamburguerTextTransform": "uppercase", - "hamburguerIconColor": "#000000", - "hamburguerHoverColor": "#e6e6e6", + "hamburgerFocusColor": "#0095ff", + "hamburgerFontFamily": "Open Sans, sans-serif", + "hamburgerFontStyle": "normal", + "hamburgerFontColor": "#000000", + "hamburgerFontSize": "10px", + "hamburgerFontWeight": "600", + "hamburgerTextTransform": "uppercase", + "hamburgerIconColor": "#000000", + "hamburgerHoverColor": "#e6e6e6", "logo": "", "logoResponsive": "", "logoHeight": "40px", diff --git a/apps/website/screens/common/themes/opinionated-theme.json b/apps/website/screens/common/themes/opinionated-theme.json index 092f4ebb58..050215d6ad 100644 --- a/apps/website/screens/common/themes/opinionated-theme.json +++ b/apps/website/screens/common/themes/opinionated-theme.json @@ -58,7 +58,7 @@ "accentColor": "#000000", "fontColor": "#000000", "menuBaseColor": "#ffffff", - "hamburguerColor": "#000000", + "hamburgerColor": "#000000", "logo": "/dxc_header_logo.svg", "logoResponsive": "/dxc_header_logo.svg", "contentColor": "#000000", diff --git a/apps/website/screens/components/accordion/code/AccordionCodePage.tsx b/apps/website/screens/components/accordion/code/AccordionCodePage.tsx index 44e31be748..38a210a296 100644 --- a/apps/website/screens/components/accordion/code/AccordionCodePage.tsx +++ b/apps/website/screens/components/accordion/code/AccordionCodePage.tsx @@ -45,7 +45,9 @@ const sections = [ boolean Initial state of the panel, only when it is uncontrolled. - - + + false + isExpanded diff --git a/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx b/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx index 23ec3c4b1a..908bbbf6f4 100644 --- a/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx +++ b/apps/website/screens/components/checkbox/code/CheckboxCodePage.tsx @@ -42,7 +42,7 @@ const sections = [ managed internally by the component. - false + - diff --git a/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx b/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx index 07d9e50616..0ea5c60f56 100644 --- a/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx +++ b/apps/website/screens/components/dialog/specs/DialogSpecsPage.tsx @@ -98,14 +98,6 @@ const sections = [ transparent - - - closeIconBorderColor - - Icon close - - - - - ), @@ -161,48 +153,6 @@ const sections = [ title: "Border", content: ( <> - - - - Component token - Element - Core token - Value - - - - - - closeIconBorderThickness - - Icon close - - border-width-0 - - 0px - - - - closeIconBorderStyle - - Icon close - - border-style-solid - - solid - - - - closeIconBorderRadius - - Icon close - - border-radius-small - - 0.125rem / 2px - - - diff --git a/apps/website/screens/components/header/specs/HeaderSpecsPage.tsx b/apps/website/screens/components/header/specs/HeaderSpecsPage.tsx index 9a9e5b8047..9414807ee6 100644 --- a/apps/website/screens/components/header/specs/HeaderSpecsPage.tsx +++ b/apps/website/screens/components/header/specs/HeaderSpecsPage.tsx @@ -80,7 +80,7 @@ const sections = [ - hamburguerHoverColor + hamburgerHoverColor Menu:hover @@ -90,7 +90,7 @@ const sections = [ - hamburguerFocusColor + hamburgerFocusColor Menu:focus @@ -100,7 +100,7 @@ const sections = [ - hamburguerFontColor + hamburgerFontColor Menu label @@ -110,7 +110,7 @@ const sections = [ - hamburguerIconColor + hamburgerIconColor Menu icon @@ -157,7 +157,7 @@ const sections = [ - hamburguerFontFamily + hamburgerFontFamily Menu label @@ -167,7 +167,7 @@ const sections = [ - hamburguerFontStyle + hamburgerFontStyle Menu label @@ -177,7 +177,7 @@ const sections = [ - hamburguerFontSize + hamburgerFontSize Menu label @@ -187,7 +187,7 @@ const sections = [ - hamburguerFontWeight + hamburgerFontWeight Menu label diff --git a/apps/website/screens/components/progress-bar/code/ProgressBarCodePage.tsx b/apps/website/screens/components/progress-bar/code/ProgressBarCodePage.tsx index 6d2350de2e..4cc180b298 100644 --- a/apps/website/screens/components/progress-bar/code/ProgressBarCodePage.tsx +++ b/apps/website/screens/components/progress-bar/code/ProgressBarCodePage.tsx @@ -63,7 +63,7 @@ const sections = [ boolean - If true, the value is displayed above the progress bar. + If true, the determined value is displayed above the progress bar. false diff --git a/apps/website/screens/components/progress-bar/code/examples/overlay.ts b/apps/website/screens/components/progress-bar/code/examples/overlay.ts index 29c7108e69..0cdd0be6f2 100644 --- a/apps/website/screens/components/progress-bar/code/examples/overlay.ts +++ b/apps/website/screens/components/progress-bar/code/examples/overlay.ts @@ -25,11 +25,10 @@ const code = `() => { label="Show Progress Bar for 3 seconds" onClick={showModal} /> - {isVisible && ( )} diff --git a/apps/website/screens/components/switch/code/SwitchCodePage.tsx b/apps/website/screens/components/switch/code/SwitchCodePage.tsx index bcb068d27c..170c598243 100644 --- a/apps/website/screens/components/switch/code/SwitchCodePage.tsx +++ b/apps/website/screens/components/switch/code/SwitchCodePage.tsx @@ -28,7 +28,9 @@ const sections = [ boolean Initial state of the switch, only when it is uncontrolled. - - + + false + checked @@ -39,9 +41,7 @@ const sections = [ If true, the component is checked. If undefined, the component will be uncontrolled and the checked attribute will be managed internally by the component. - - false - + - value diff --git a/apps/website/screens/components/toast/code/ToastCodePage.tsx b/apps/website/screens/components/toast/code/ToastCodePage.tsx index b9c45f2a70..3ac8256f14 100644 --- a/apps/website/screens/components/toast/code/ToastCodePage.tsx +++ b/apps/website/screens/components/toast/code/ToastCodePage.tsx @@ -49,6 +49,14 @@ const sections = [ 3000 + + children + + ReactNode + + Tree of components from which the useToast hook can be triggered. + - + ), diff --git a/apps/website/screens/components/wizard/code/WizardCodePage.tsx b/apps/website/screens/components/wizard/code/WizardCodePage.tsx index 86885472a4..acb4d9f9bb 100644 --- a/apps/website/screens/components/wizard/code/WizardCodePage.tsx +++ b/apps/website/screens/components/wizard/code/WizardCodePage.tsx @@ -30,7 +30,9 @@ const sections = [ number Initially selected step, only when it is uncontrolled. - - + + 0 + currentStep @@ -41,9 +43,7 @@ const sections = [ Defines which step is marked as the current. The numeration starts at 0. If undefined, the component will be uncontrolled and the step will be managed internally by the component. - - 0 - + - mode diff --git a/apps/website/screens/principles/localization/LocalizationPage.tsx b/apps/website/screens/principles/localization/LocalizationPage.tsx index 29b25c2b08..20c37536e1 100644 --- a/apps/website/screens/principles/localization/LocalizationPage.tsx +++ b/apps/website/screens/principles/localization/LocalizationPage.tsx @@ -353,7 +353,7 @@ const sections = [ - hamburguerTitle + hamburgerTitle Menu diff --git a/apps/website/screens/principles/themes/ThemesPage.tsx b/apps/website/screens/principles/themes/ThemesPage.tsx index 760f6ee9fb..ddeba37ef5 100644 --- a/apps/website/screens/principles/themes/ThemesPage.tsx +++ b/apps/website/screens/principles/themes/ThemesPage.tsx @@ -671,7 +671,7 @@ const sections = [ Font color - hamburguerFontColor + hamburgerFontColor @@ -681,12 +681,12 @@ const sections = [ - Hamburguer color + Hamburger color - hamburguerIconColor + hamburgerIconColor

- hamburguerHoverColor (+90% of lightness) + hamburgerHoverColor (+90% of lightness) diff --git a/apps/website/screens/principles/themes/examples/bloomTheme.ts b/apps/website/screens/principles/themes/examples/bloomTheme.ts index 970f2046dd..461419993b 100644 --- a/apps/website/screens/principles/themes/examples/bloomTheme.ts +++ b/apps/website/screens/principles/themes/examples/bloomTheme.ts @@ -40,7 +40,7 @@ export default { accentColor: "#000000", fontColor: "#000000", menuBaseColor: "#ffffff", - hamburguerColor: "#000000", + hamburgerColor: "#000000", logo: "https://assure.proxy.lambda/image/image_1674206652560.jpg", logoResponsive: "https://assure.proxy.lambda/image/image_1674206660896.jpg", contentColor: "#000000", diff --git a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json index cbc0944068..ed36d5387f 100644 --- a/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json +++ b/apps/website/screens/theme-generator/themes/schemas/advanced.schema.json @@ -404,14 +404,8 @@ "overlayColor": "color", "backgroundColor": "color", "closeIconSize": "length", - "closeIconTopPosition": "length", - "closeIconRightPosition": "length", "closeIconBackgroundColor": "color", "closeIconColor": "color", - "closeIconBorderColor": "color", - "closeIconBorderThickness": "bWidth", - "closeIconBorderStyle": "bStyle", - "closeIconBorderRadius": "length", "boxShadowOffsetX": "length", "boxShadowOffsetY": "length", "boxShadowBlur": "length", @@ -548,13 +542,13 @@ "backgroundColor": "color", "underlinedColor": "color", "menuBackgroundColor": "color", - "hamburguerIconColor": "color", - "hamburguerHoverColor": "color", + "hamburgerIconColor": "color", + "hamburgerHoverColor": "color", "overlayColor": "color", - "hamburguerFocusColor": "color", + "hamburgerFocusColor": "color", "logo": "image", "logoResponsive": "image", - "hamburguerTextTransform": "fTextTransform", + "hamburgerTextTransform": "fTextTransform", "underlinedThickness": "bWidth", "underlinedStyle": "bStyle", "minHeight": "length", @@ -569,11 +563,11 @@ "menuMobileWidth": "length", "overlayOpacity": "alphaValue", "overlayZindex": "integer", - "hamburguerFontFamily": "fFamily", - "hamburguerFontStyle": "fStyle", - "hamburguerFontColor": "color", - "hamburguerFontSize": "length", - "hamburguerFontWeight": "fWeight", + "hamburgerFontFamily": "fFamily", + "hamburgerFontStyle": "fStyle", + "hamburgerFontColor": "color", + "hamburgerFontSize": "length", + "hamburgerFontWeight": "fWeight", "contentColor": "color" }, "heading": { diff --git a/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json b/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json index f766b055d2..f61d8dd52f 100644 --- a/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json +++ b/apps/website/screens/theme-generator/themes/schemas/opinionated.schema.json @@ -58,7 +58,7 @@ "accentColor": "color", "fontColor": "color", "menuBaseColor": "color", - "hamburguerColor": "color", + "hamburgerColor": "color", "logo": "image", "logoResponsive": "image", "contentColor": "color", diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 69693bf199..500f2e7a86 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -190,10 +190,10 @@ const parseTheme = (theme: DeepPartial): AdvancedTheme => { headerTokens.backgroundColor = theme.header?.baseColor ?? headerTokens.backgroundColor; headerTokens.underlinedColor = theme.header?.accentColor ?? headerTokens.underlinedColor; headerTokens.menuBackgroundColor = theme.header?.menuBaseColor ?? headerTokens.menuBackgroundColor; - headerTokens.hamburguerFontColor = theme.header?.fontColor ?? headerTokens.hamburguerFontColor; - headerTokens.hamburguerIconColor = theme.header?.hamburguerColor ?? headerTokens.hamburguerIconColor; - headerTokens.hamburguerHoverColor = - addLightness(90, theme.header?.hamburguerColor) ?? headerTokens.hamburguerHoverColor; + headerTokens.hamburgerFontColor = theme.header?.fontColor ?? headerTokens.hamburgerFontColor; + headerTokens.hamburgerIconColor = theme.header?.hamburgerColor ?? headerTokens.hamburgerIconColor; + headerTokens.hamburgerHoverColor = + addLightness(90, theme.header?.hamburgerColor) ?? headerTokens.hamburgerHoverColor; headerTokens.logo = theme.header?.logo ?? headerTokens.logo; headerTokens.logoResponsive = theme.header?.logoResponsive ?? headerTokens.logoResponsive; headerTokens.contentColor = theme.header?.contentColor ?? headerTokens.contentColor; diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index 5d576f6b94..cbdea4f20b 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -6,68 +6,6 @@ import HalstackContext from "../HalstackContext"; import AccordionPropsType from "./types"; import DxcIcon from "../icon/Icon"; -const DxcAccordion = ({ - label = "", - defaultIsExpanded, - isExpanded, - icon, - assistiveText = "", - disabled = false, - onChange, - children, - margin, - tabIndex = 0, -}: AccordionPropsType): JSX.Element => { - const id = useId(); - const [innerIsExpanded, setInnerIsExpanded] = useState(defaultIsExpanded ?? false); - const colorsTheme = useContext(HalstackContext); - - const handleAccordionState = () => { - if (isExpanded == null) { - setInnerIsExpanded((innerIsCurrentlyExpanded) => !innerIsCurrentlyExpanded); - } - onChange?.(isExpanded != null ? !isExpanded : !innerIsExpanded); - }; - - return ( - - - - - - - {icon && ( - - {typeof icon === "string" ? : icon} - - )} - {label} - - {assistiveText && {assistiveText}} - - - - - - - {(isExpanded ?? innerIsExpanded) && ( - - {children} - - )} - - - ); -}; - const calculateWidth = (margin: AccordionPropsType["margin"]) => `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; @@ -191,4 +129,66 @@ const AccordionPanel = styled.div` border-bottom-right-radius: ${(props) => props.theme.borderRadius}; `; +const DxcAccordion = ({ + label = "", + defaultIsExpanded = false, + isExpanded, + icon, + assistiveText = "", + disabled = false, + onChange, + children, + margin, + tabIndex = 0, +}: AccordionPropsType): JSX.Element => { + const id = useId(); + const [innerIsExpanded, setInnerIsExpanded] = useState(defaultIsExpanded); + const colorsTheme = useContext(HalstackContext); + + const handleAccordionState = () => { + if (isExpanded == null) { + setInnerIsExpanded((innerIsCurrentlyExpanded) => !innerIsCurrentlyExpanded); + } + onChange?.(isExpanded != null ? !isExpanded : !innerIsExpanded); + }; + + return ( + + + + + + + {icon && ( + + {typeof icon === "string" ? : icon} + + )} + {label} + + {assistiveText && {assistiveText}} + + + + + + + {(isExpanded ?? innerIsExpanded) && ( + + {children} + + )} + + + ); +}; + export default DxcAccordion; diff --git a/packages/lib/src/alert/types.ts b/packages/lib/src/alert/types.ts index 71c6643784..5985269862 100644 --- a/packages/lib/src/alert/types.ts +++ b/packages/lib/src/alert/types.ts @@ -2,31 +2,87 @@ import { ReactNode } from "react"; import { SVG } from "../common/utils"; type Action = { + /** + * The icon of the action. It can be a string with the name of the icon or an SVG component. + */ icon?: string | SVG; + /** + * The label of the action. + */ label: string; + /** + * The function that will be executed when the user clicks on the action button. + */ onClick: () => void; }; type Message = { + /** + * The function that will be executed when the user clicks on the close action button. + */ onClose?: () => void; + /** + * The content of the message. The only Halstack component allowed within the text of an alert is the Link component, + * and it should be used exclusively to direct users to additional resources or relevant pages. + * No other components are permitted within the content of an alert. + */ text: ReactNode; }; type CommonProps = { + /** + * If true, the alert will have a close button that will remove the message from the alert, + * only in banner and inline modes. The rest of the functionality will depend + * on the onClose event of each message (e.g. closing the modal alert). + */ closable?: boolean; + /** + * Primary action of the alert. + */ primaryAction?: Action; + /** + * Secondary action of the alert. + */ secondaryAction?: Action; + /** + * Specifies the semantic meaning of the alert. + */ semantic?: "error" | "info" | "success" | "warning"; + /** + * Title of the alert. + */ title: string; }; type ModeSpecificProps = | { + /** + * List of messages to be displayed. Each message has a close action that will, + * apart from remove from the alert the current message, call the onClose if it is defined. + * If the message is an array, the alert will show a navigation bar to move between the messages. + * The modal mode only allows one message per alert. In this case, the message must be an object + * and the presence of the onClose attribute is mandatory, since the management of the closing + * of the alert relies completely on the user. + */ message?: Message | Message[]; + /** + * The mode of the alert. + */ mode?: "inline" | "banner"; } | { + /** + * List of messages to be displayed. Each message has a close action that will, + * apart from remove from the alert the current message, call the onClose if it is defined. + * If the message is an array, the alert will show a navigation bar to move between the messages. + * The modal mode only allows one message per alert. In this case, the message must be an object + * and the presence of the onClose attribute is mandatory, since the management of the closing + * of the alert relies completely on the user. + */ message: Required; + /** + * The mode of the alert. + */ mode: "modal"; }; diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts index 6990e4dbe3..1733102ad2 100644 --- a/packages/lib/src/common/variables.ts +++ b/packages/lib/src/common/variables.ts @@ -389,14 +389,8 @@ export const componentTokens = { overlayColor: CoreTokens.color_grey_800_a, backgroundColor: CoreTokens.color_white, closeIconSize: "24px", - closeIconTopPosition: "20px", - closeIconRightPosition: "20px", closeIconBackgroundColor: CoreTokens.color_transparent, - closeIconBorderColor: CoreTokens.border_none, closeIconColor: CoreTokens.color_black, - closeIconBorderThickness: CoreTokens.border_width_0, - closeIconBorderStyle: CoreTokens.border_solid, - closeIconBorderRadius: "2px", boxShadowOffsetX: "0px", boxShadowOffsetY: "1px", boxShadowBlur: "3px", @@ -531,15 +525,15 @@ export const componentTokens = { }, header: { backgroundColor: CoreTokens.color_white, - hamburguerFocusColor: CoreTokens.color_blue_600, - hamburguerFontFamily: CoreTokens.type_sans, - hamburguerFontStyle: CoreTokens.type_normal, - hamburguerFontColor: CoreTokens.color_black, - hamburguerFontSize: "10px", - hamburguerFontWeight: CoreTokens.type_semibold, - hamburguerTextTransform: CoreTokens.type_uppercase, - hamburguerIconColor: CoreTokens.color_black, - hamburguerHoverColor: CoreTokens.color_grey_200, + hamburgerFocusColor: CoreTokens.color_blue_600, + hamburgerFontFamily: CoreTokens.type_sans, + hamburgerFontStyle: CoreTokens.type_normal, + hamburgerFontColor: CoreTokens.color_black, + hamburgerFontSize: "10px", + hamburgerFontWeight: CoreTokens.type_semibold, + hamburgerTextTransform: CoreTokens.type_uppercase, + hamburgerIconColor: CoreTokens.color_black, + hamburgerHoverColor: CoreTokens.color_grey_200, logo: "", logoResponsive: "", logoHeight: "40px", @@ -1353,7 +1347,7 @@ export type OpinionatedTheme = { accentColor: string; fontColor: string; menuBaseColor: string; - hamburguerColor: string; + hamburgerColor: string; logo: string; logoResponsive: string; contentColor: string; @@ -1519,7 +1513,7 @@ export const defaultTranslatedComponentLabels = { }, header: { closeIcon: "Close menu", - hamburguerTitle: "Menu", + hamburgerTitle: "Menu", }, numberInput: { valueGreaterThanOrEqualToErrorMessage: (value: number) => `Value must be greater than or equal to ${value}.`, diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index d86d7f6412..d6df6e543b 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect } from "react"; import { createPortal } from "react-dom"; import styled, { createGlobalStyle, ThemeProvider } from "styled-components"; import { responsiveSizes } from "../common/variables"; -import DxcIcon from "../icon/Icon"; +import DxcActionIcon from "../action-icon/ActionIcon"; import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import FocusLock from "../utils/FocusLock"; import DialogPropsType from "./types"; @@ -48,37 +48,10 @@ const Dialog = styled.div<{ closable: DialogPropsType["closable"] }>` } `; -const CloseIconAction = styled.button` - all: unset; +const CloseIconActionContainer = styled.div` position: absolute; top: 24px; right: 24px; - display: flex; - align-items: center; - justify-content: center; - background-color: ${(props) => props.theme.closeIconBackgroundColor}; - box-shadow: 0 0 0 2px transparent; - color: ${(props) => props.theme.closeIconColor}; - border-radius: ${(props) => props.theme.closeIconBorderRadius}; - border-width: ${(props) => props.theme.closeIconBorderThickness}; - border-style: ${(props) => props.theme.closeIconBorderStyle}; - border-color: ${(props) => props.theme.closeIconBorderColor}; - cursor: pointer; - z-index: 1; - - &:focus { - outline: none; - box-shadow: 0 0 0 2px #0095ff; - } - &:hover { - background-color: #f2f2f2; - } - &:active { - background-color: #cccccc; - } - span { - font-size: ${(props) => props.theme.closeIconSize}; - } `; const DxcDialog = ({ @@ -111,27 +84,25 @@ const DxcDialog = ({ {createPortal( - {overlay && ( - { - onBackgroundClick?.(); - }} - /> - )} - + {overlay && } + {children} - {closable && ( - { - onCloseClick?.(); - }} - aria-label={translatedLabels.dialog.closeIconAriaLabel} - tabIndex={tabIndex} - > - - - )} + + + + + , diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index 0a8d1a64ec..2bc4fae13b 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -5,32 +5,127 @@ import { spaces } from "../common/variables"; import FileItem from "./FileItem"; import FileInputPropsType, { FileData, RefType } from "./types"; import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; +import { getFilePreview, isFileIncluded } from "./utils"; -const getFilePreview = async (file: File): Promise => { - if (file.type.includes("video")) return "filled_movie"; - else if (file.type.includes("audio")) return "music_video"; - else if (file.type.includes("image")) { - return new Promise((resolve) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = (e) => { - resolve(e.target?.result as string); - }; - }); - } else return "draft"; -}; +const FileInputContainer = styled.div<{ margin: FileInputPropsType["margin"] }>` + display: flex; + flex-direction: column; + margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; + margin-top: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; + margin-right: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.right ? spaces[props.margin.right] : ""}; + margin-bottom: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.bottom ? spaces[props.margin.bottom] : ""}; + margin-left: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; + width: fit-content; +`; + +const Label = styled.label<{ disabled: FileInputPropsType["disabled"] }>` + color: ${(props) => (props.disabled ? props.theme.disabledLabelFontColor : props.theme.labelFontColor)}; + font-family: ${(props) => props.theme.labelFontFamily}; + font-size: ${(props) => props.theme.labelFontSize}; + font-weight: ${(props) => props.theme.labelFontWeight}; + line-height: ${(props) => props.theme.labelLineHeight}; +`; + +const HelperText = styled.span<{ disabled: FileInputPropsType["disabled"] }>` + color: ${(props) => (props.disabled ? props.theme.disabledHelperTextFontColor : props.theme.helperTextFontColor)}; + font-family: ${(props) => props.theme.helperTextFontFamily}; + font-size: ${(props) => props.theme.helperTextFontSize}; + font-weight: ${(props) => props.theme.helperTextFontWeight}; + line-height: ${(props) => props.theme.helperTextLineHeight}; +`; -const isFileIncluded = (file: FileData, fileList: FileData[]) => { - const fileListInfo = fileList.map((existingFile) => existingFile.file); - return fileListInfo.some( - ({ name, size, type, lastModified, webkitRelativePath }) => - name === file.file.name && - size === file.file.size && - type === file.file.type && - lastModified === file.file.lastModified && - webkitRelativePath === file.file.webkitRelativePath - ); -}; +const FileContainer = styled.div<{ singleFileMode: boolean }>` + display: flex; + ${(props) => + props.singleFileMode ? "flex-direction: row; column-gap: 0.25rem;" : "flex-direction: column; row-gap: 0.25rem;"} + margin-top: 0.25rem; +`; + +const ValueInput = styled.input` + display: none; +`; + +const FileItemListContainer = styled.div` + display: flex; + flex-direction: column; + row-gap: 0.25rem; +`; + +const Container = styled.div` + display: flex; + flex-direction: column; + row-gap: 0.25rem; + margin-top: 0.25rem; +`; + +const DragDropArea = styled.div<{ + mode: FileInputPropsType["mode"]; + disabled: FileInputPropsType["disabled"]; + isDragging: boolean; +}>` + box-sizing: border-box; + display: flex; + ${(props) => + props.mode === "filedrop" + ? "flex-direction: row; column-gap: 0.75rem; height: 48px;" + : "justify-content: center; flex-direction: column; row-gap: 0.5rem; height: 160px;"} + align-items: center; + width: 320px; + padding: ${(props) => + props.mode === "filedrop" + ? `calc(4px - ${props.theme.dropBorderThickness}) 1rem calc(4px - ${props.theme.dropBorderThickness}) calc(4px - ${props.theme.dropBorderThickness})` + : "1rem"}; + overflow: hidden; + box-shadow: 0 0 0 2px transparent; + border-radius: ${(props) => props.theme.dropBorderRadius}; + border-width: ${(props) => props.theme.dropBorderThickness}; + border-style: ${(props) => props.theme.dropBorderStyle}; + border-color: ${(props) => (props.disabled ? props.theme.disabledDropBorderColor : props.theme.dropBorderColor)}; + ${(props) => + props.isDragging && + ` + background-color: ${props.theme.dragoverDropBackgroundColor}; + border-color: transparent; + box-shadow: 0 0 0 2px ${props.theme.focusDropBorderColor}; + `} + cursor: ${(props) => props.disabled && "not-allowed"}; +`; + +const DropzoneLabel = styled.div<{ disabled: FileInputPropsType["disabled"] }>` + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 3; + text-align: center; + color: ${(props) => (props.disabled ? props.theme.disabledDropLabelFontColor : props.theme.dropLabelFontColor)}; + font-family: ${(props) => props.theme.dropLabelFontFamily}; + font-size: ${(props) => props.theme.dropLabelFontSize}; + font-weight: ${(props) => props.theme.dropLabelFontWeight}; +`; + +const FiledropLabel = styled.span<{ disabled: FileInputPropsType["disabled"] }>` + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + color: ${(props) => (props.disabled ? props.theme.disabledDropLabelFontColor : props.theme.dropLabelFontColor)}; + font-family: ${(props) => props.theme.dropLabelFontFamily}; + font-size: ${(props) => props.theme.dropLabelFontSize}; + font-weight: ${(props) => props.theme.dropLabelFontWeight}; +`; + +const ErrorMessage = styled.div` + color: ${(props) => props.theme.errorMessageFontColor}; + font-family: ${(props) => props.theme.errorMessageFontFamily}; + font-size: ${(props) => props.theme.errorMessageFontSize}; + font-weight: ${(props) => props.theme.errorMessageFontWeight}; + line-height: ${(props) => props.theme.errorMessageLineHeight}; + margin-top: 0.25rem; +`; const DxcFileInput = forwardRef( ( @@ -139,7 +234,7 @@ const DxcFileInput = forwardRef( }; useEffect(() => { - const getFiles = async () => { + (async () => { if (value) { const valueFiles = (await Promise.all( value.map(async (file) => { @@ -152,8 +247,7 @@ const DxcFileInput = forwardRef( )) as FileData[]; setFiles(valueFiles); } - }; - getFiles(); + })(); }, [value]); return ( @@ -276,124 +370,4 @@ const DxcFileInput = forwardRef( } ); -const FileInputContainer = styled.div<{ margin: FileInputPropsType["margin"] }>` - display: flex; - flex-direction: column; - margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; - margin-top: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; - margin-right: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.right ? spaces[props.margin.right] : ""}; - margin-bottom: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.bottom ? spaces[props.margin.bottom] : ""}; - margin-left: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; - width: fit-content; -`; - -const Label = styled.label<{ disabled: FileInputPropsType["disabled"] }>` - color: ${(props) => (props.disabled ? props.theme.disabledLabelFontColor : props.theme.labelFontColor)}; - font-family: ${(props) => props.theme.labelFontFamily}; - font-size: ${(props) => props.theme.labelFontSize}; - font-weight: ${(props) => props.theme.labelFontWeight}; - line-height: ${(props) => props.theme.labelLineHeight}; -`; - -const HelperText = styled.span<{ disabled: FileInputPropsType["disabled"] }>` - color: ${(props) => (props.disabled ? props.theme.disabledHelperTextFontColor : props.theme.helperTextFontColor)}; - font-family: ${(props) => props.theme.helperTextFontFamily}; - font-size: ${(props) => props.theme.helperTextFontSize}; - font-weight: ${(props) => props.theme.helperTextFontWeight}; - line-height: ${(props) => props.theme.helperTextLineHeight}; -`; - -const FileContainer = styled.div<{ singleFileMode: boolean }>` - display: flex; - ${(props) => - props.singleFileMode ? "flex-direction: row; column-gap: 0.25rem;" : "flex-direction: column; row-gap: 0.25rem;"} - margin-top: 0.25rem; -`; - -const ValueInput = styled.input` - display: none; -`; - -const FileItemListContainer = styled.div` - display: flex; - flex-direction: column; - row-gap: 0.25rem; -`; - -const Container = styled.div` - display: flex; - flex-direction: column; - row-gap: 0.25rem; - margin-top: 0.25rem; -`; - -const DragDropArea = styled.div<{ - mode: FileInputPropsType["mode"]; - disabled: FileInputPropsType["disabled"]; - isDragging: boolean; -}>` - box-sizing: border-box; - display: flex; - ${(props) => - props.mode === "filedrop" - ? "flex-direction: row; column-gap: 0.75rem; height: 48px;" - : "justify-content: center; flex-direction: column; row-gap: 0.5rem; height: 160px;"} - align-items: center; - width: 320px; - padding: ${(props) => - props.mode === "filedrop" - ? `calc(4px - ${props.theme.dropBorderThickness}) 1rem calc(4px - ${props.theme.dropBorderThickness}) calc(4px - ${props.theme.dropBorderThickness})` - : "1rem"}; - overflow: hidden; - box-shadow: 0 0 0 2px transparent; - border-radius: ${(props) => props.theme.dropBorderRadius}; - border-width: ${(props) => props.theme.dropBorderThickness}; - border-style: ${(props) => props.theme.dropBorderStyle}; - border-color: ${(props) => (props.disabled ? props.theme.disabledDropBorderColor : props.theme.dropBorderColor)}; - ${(props) => - props.isDragging && - ` - background-color: ${props.theme.dragoverDropBackgroundColor}; - border-color: transparent; - box-shadow: 0 0 0 2px ${props.theme.focusDropBorderColor}; - `} - cursor: ${(props) => props.disabled && "not-allowed"}; -`; - -const DropzoneLabel = styled.div<{ disabled: FileInputPropsType["disabled"] }>` - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 3; - text-align: center; - color: ${(props) => (props.disabled ? props.theme.disabledDropLabelFontColor : props.theme.dropLabelFontColor)}; - font-family: ${(props) => props.theme.dropLabelFontFamily}; - font-size: ${(props) => props.theme.dropLabelFontSize}; - font-weight: ${(props) => props.theme.dropLabelFontWeight}; -`; - -const FiledropLabel = styled.span<{ disabled: FileInputPropsType["disabled"] }>` - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - color: ${(props) => (props.disabled ? props.theme.disabledDropLabelFontColor : props.theme.dropLabelFontColor)}; - font-family: ${(props) => props.theme.dropLabelFontFamily}; - font-size: ${(props) => props.theme.dropLabelFontSize}; - font-weight: ${(props) => props.theme.dropLabelFontWeight}; -`; - -const ErrorMessage = styled.div` - color: ${(props) => props.theme.errorMessageFontColor}; - font-family: ${(props) => props.theme.errorMessageFontFamily}; - font-size: ${(props) => props.theme.errorMessageFontSize}; - font-weight: ${(props) => props.theme.errorMessageFontWeight}; - line-height: ${(props) => props.theme.errorMessageLineHeight}; - margin-top: 0.25rem; -`; - export default DxcFileInput; diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index 1175f483a9..5de65801b7 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -1,4 +1,4 @@ -import { memo, useContext } from "react"; +import { memo, useContext, useId } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; import { FileItemProps } from "./types"; @@ -6,58 +6,6 @@ import DxcIcon from "../icon/Icon"; import DxcActionIcon from "../action-icon/ActionIcon"; import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; -const FileItem = ({ - fileName = "", - error = "", - singleFileMode, - showPreview, - preview, - type, - onDelete, - tabIndex, -}: FileItemProps): JSX.Element => { - const colorsTheme = useContext(HalstackContext); - const translatedLabels = useContext(HalstackLanguageContext); - - const getIconAriaLabel = () => (type.includes("video") ? "video" : type.includes("audio") ? "audio" : "file"); - - return ( - - - {showPreview && - (type.includes("image") ? ( - - ) : ( - - - - ))} - - {fileName} - - {error && ( - - - - )} - onDelete(fileName)} - icon="close" - tabIndex={tabIndex} - title={translatedLabels.fileInput.deleteFileActionTitle} - /> - - {error && !singleFileMode && ( - - {error} - - )} - - - - ); -}; - const MainContainer = styled.div<{ error: FileItemProps["error"]; singleFileMode: FileItemProps["singleFileMode"]; @@ -144,4 +92,56 @@ const ErrorMessage = styled.span` line-height: ${(props) => props.theme.errorMessageLineHeight}; `; +const FileItem = ({ + fileName = "", + error = "", + singleFileMode, + showPreview, + preview, + type, + onDelete, + tabIndex, +}: FileItemProps): JSX.Element => { + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); + + const fileNameId = useId(); + + return ( + + + {showPreview && + (type.includes("image") ? ( + + ) : ( + + + + ))} + + {fileName} + + {error && ( + + + + )} + onDelete(fileName)} + icon="close" + tabIndex={tabIndex} + title={translatedLabels.fileInput.deleteFileActionTitle} + /> + + {error && !singleFileMode && ( + + {error} + + )} + + + + ); +}; + export default memo(FileItem); diff --git a/packages/lib/src/file-input/utils.ts b/packages/lib/src/file-input/utils.ts new file mode 100644 index 0000000000..7b76761e73 --- /dev/null +++ b/packages/lib/src/file-input/utils.ts @@ -0,0 +1,27 @@ +import { FileData } from "./types"; + +export const getFilePreview = async (file: File): Promise => { + if (file.type.includes("video")) return "filled_movie"; + else if (file.type.includes("audio")) return "music_video"; + else if (file.type.includes("image")) { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = (e) => { + resolve(e.target?.result as string); + }; + }); + } else return "draft"; +}; + +export const isFileIncluded = (file: FileData, fileList: FileData[]) => { + const fileListInfo = fileList.map((existingFile) => existingFile.file); + return fileListInfo.some( + ({ name, size, type, lastModified, webkitRelativePath }) => + name === file.file.name && + size === file.file.size && + type === file.file.type && + lastModified === file.file.lastModified && + webkitRelativePath === file.file.webkitRelativePath + ); +}; \ No newline at end of file diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx index e7b0ccf14a..4c76a3b6d7 100644 --- a/packages/lib/src/header/Header.stories.tsx +++ b/packages/lib/src/header/Header.stories.tsx @@ -57,7 +57,7 @@ const opinionatedTheme = { accentColor: "#000000", fontColor: "#000000", menuBaseColor: "#ffffff", - hamburguerColor: "#000000", + hamburgerColor: "#000000", logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/2021_Facebook_icon.svg/2048px-2021_Facebook_icon.svg.png", logoResponsive: "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/2021_Facebook_icon.svg/2048px-2021_Facebook_icon.svg.png", diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index ba23eec1de..2dd9784283 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -115,7 +115,7 @@ const DxcHeader = ({ - {translatedLabels.header.hamburguerTitle} + {translatedLabels.header.hamburgerTitle} @@ -224,23 +224,23 @@ const HamburgerTrigger = styled.button` border-radius: 2px; background-color: transparent; :hover { - background-color: ${(props) => props.theme.hamburguerHoverColor}; + background-color: ${(props) => props.theme.hamburgerHoverColor}; } &:focus { - outline: ${(props) => props.theme.hamburguerFocusColor} auto 1px; + outline: ${(props) => props.theme.hamburgerFocusColor} auto 1px; } & > svg { - fill: ${(props) => props.theme.hamburguerIconColor}; + fill: ${(props) => props.theme.hamburgerIconColor}; } & > span { font-size: 24px; } - font-family: ${(props) => props.theme.hamburguerFontFamily}; - font-style: ${(props) => props.theme.hamburguerFontStyle}; - font-size: ${(props) => props.theme.hamburguerFontSize}; - text-transform: ${(props) => props.theme.hamburguerTextTransform}; - font-weight: ${(props) => props.theme.hamburguerFontWeight}; - color: ${(props) => props.theme.hamburguerFontColor}; + font-family: ${(props) => props.theme.hamburgerFontFamily}; + font-style: ${(props) => props.theme.hamburgerFontStyle}; + font-size: ${(props) => props.theme.hamburgerFontSize}; + text-transform: ${(props) => props.theme.hamburgerTextTransform}; + font-weight: ${(props) => props.theme.hamburgerFontWeight}; + color: ${(props) => props.theme.hamburgerFontColor}; `; const ResponsiveMenu = styled.div<{ hasVisibility: boolean }>` @@ -288,7 +288,7 @@ const CloseAction = styled.button` :focus, :focus-visible { - outline: ${(props) => props.theme.hamburguerFocusColor} auto 1px; + outline: ${(props) => props.theme.hamburgerFocusColor} auto 1px; } font-size: 24px; svg { diff --git a/packages/lib/src/icon/Icon.tsx b/packages/lib/src/icon/Icon.tsx index d49b99db7a..cc2b977e6f 100644 --- a/packages/lib/src/icon/Icon.tsx +++ b/packages/lib/src/icon/Icon.tsx @@ -3,7 +3,6 @@ import styled from "styled-components"; const DxcIcon = ({ icon }: { icon: string }): JSX.Element => (