diff --git a/apps/website/pages/components/date-input/code.tsx b/apps/website/pages/components/date-input/code.tsx new file mode 100644 index 0000000000..29148ee61e --- /dev/null +++ b/apps/website/pages/components/date-input/code.tsx @@ -0,0 +1,19 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import DateInputCodePage from "screens/components/date-input/code/DateInputCodePage"; +import DateInputPageLayout from "screens/components/date-input/DateInputPageLayout"; + +const Code = () => { + return ( + <> + + Date Input Code — Halstack Design System + + + + ); +}; + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/date-input/index.tsx b/apps/website/pages/components/date-input/index.tsx index c473cb2cd5..50bcf3a768 100644 --- a/apps/website/pages/components/date-input/index.tsx +++ b/apps/website/pages/components/date-input/index.tsx @@ -1,21 +1,19 @@ import Head from "next/head"; import type { ReactElement } from "react"; -import DateInputCodePage from "screens/components/date-input/code/DateInputCodePage"; +import DateInputOverviewPage from "screens/components/date-input/overview/DateInputOverviewPage"; import DateInputPageLayout from "screens/components/date-input/DateInputPageLayout"; -const Usage = () => { +const Index = () => { return ( <> Date Input — Halstack Design System - + ); }; -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; -export default Usage; +export default Index; diff --git a/apps/website/pages/components/date-input/specifications.tsx b/apps/website/pages/components/date-input/specifications.tsx deleted file mode 100644 index a778ec8282..0000000000 --- a/apps/website/pages/components/date-input/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import DateInputSpecsPage from "screens/components/date-input/specs/DateInputSpecsPage"; -import DateInputPageLayout from "screens/components/date-input/DateInputPageLayout"; - -const Specifications = () => { - return ( - <> - - Date Input Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/date-input/usage.tsx b/apps/website/pages/components/date-input/usage.tsx deleted file mode 100644 index da5dde9f9e..0000000000 --- a/apps/website/pages/components/date-input/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import DateInputUsagePage from "screens/components/date-input/usage/DateInputUsagePage"; -import DateInputPageLayout from "screens/components/date-input/DateInputPageLayout"; - -const Usage = () => { - return ( - <> - - Date Input Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/date-input/DateInputPageLayout.tsx b/apps/website/screens/components/date-input/DateInputPageLayout.tsx index 2e72577cdf..169138a298 100644 --- a/apps/website/screens/components/date-input/DateInputPageLayout.tsx +++ b/apps/website/screens/components/date-input/DateInputPageLayout.tsx @@ -6,9 +6,8 @@ import { ReactNode } from "react"; const DateInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/date-input" }, - { label: "Usage", path: "/components/date-input/usage" }, - { label: "Specifications", path: "/components/date-input/specifications" }, + { label: "Overview", path: "/components/date-input" }, + { label: "Code", path: "/components/date-input/code" }, ]; return ( @@ -16,10 +15,8 @@ const DateInputPageHeading = ({ children }: { children: ReactNode }) => { - - A date input is a user interface element where the user can type or select a date in a predefined format. - - + Date inputs enable users to type or select a date in a predefined format. + {children} diff --git a/apps/website/screens/components/date-input/code/DateInputCodePage.tsx b/apps/website/screens/components/date-input/code/DateInputCodePage.tsx index de5c5361df..08f18dda49 100644 --- a/apps/website/screens/components/date-input/code/DateInputCodePage.tsx +++ b/apps/website/screens/components/date-input/code/DateInputCodePage.tsx @@ -25,58 +25,73 @@ const sections = [ - defaultValue + ariaLabel string - Initial value of the input element, only when it is uncontrolled. - - + + Specifies a string to be used as the name for the date input element when no label is + provided. + + 'Date input' - value + autocomplete string - Value of the input element. If undefined, the component will be uncontrolled and the value will be managed - internally by the component. + HTML autocomplete attribute. Lets the user specify if any permission the user agent has to + provide automated assistance in filling out the input value. Its value must be one of all the possible + values of the HTML autocomplete attribute. See{" "} + MDN{" "} + for further information. - - - - - label - string + 'off' - Text to be placed above the date input. - - - name + clearable - string + boolean + + If true, the date input will have an action to clear the entered value. + + false - Name attribute of the input element. - - - helperText + defaultValue string - Helper text to be placed above the date. + Initial value of the input element, only when it is uncontrolled. - - placeholder + disabled boolean - If true, the date format will appear as placeholder in the field. + If true, the component will be disabled. false + + error + + string + + + If it is a defined value and also a truthy string, the component will change its appearance, showing the + error below the date input component. If the defined value is an empty string, it will reserve a space + below the component for a future error, but it would not change its look. In case of being undefined or + null, both the appearance and the space for the error message would not be modified. + + - + format @@ -93,52 +108,52 @@ const sections = [ - clearable - - boolean - - If true, the date input will have an action to clear the entered value. + helperText - false + string + Helper text to be placed above the date. + - - disabled - - boolean - - If true, the component will be disabled. + label - false + string + Text to be placed above the date input. + - - optional - - boolean - + margin - If true, the date will be optional, showing the text '(Optional)' next to the label. Otherwise, the field - will be considered required and an error will be passed as a parameter to the onBlur and{" "} - onChange functions when it has not been filled. + 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | Margin - false + Size of the margin to be applied to the component. You can pass an object with 'top', 'bottom', 'left' and + 'right' properties in order to specify different margin sizes. + - - readOnly + name - boolean + string + Name attribute of the input element. + - + + + onBlur - If true, the component will not be mutable, meaning the user can not edit the control. The date picker - cannot be opened either. In addition, the clear action will not be displayed even if the flag is set to - true. + {"(val: { value: string; error?: string; date?: Date }) => void"} - false + This function will be called when the input element loses the focus. An object including the string value, + the error and the date value will be passed to this function. If the string value is a valid date,{" "} + error will be undefined. Also, if the string value is not a valid date, date{" "} + will be undefined. + - onChange @@ -154,56 +169,49 @@ const sections = [ - - onBlur + optional - {"(val: { value: string; error?: string; date?: Date }) => void"} + boolean - This function will be called when the input element loses the focus. An object including the string value, - the error and the date value will be passed to this function. If the string value is a valid date,{" "} - error will be undefined. Also, if the string value is not a valid date, date{" "} - will be undefined. + If true, the date will be optional, showing the text '(Optional)' next to the label. Otherwise, the field + will be considered required and an error will be passed as a parameter to the onBlur and{" "} + onChange functions when it has not been filled. + + + false - - - error + placeholder - string + boolean + If true, the date format will appear as placeholder in the field. - If it is a defined value and also a truthy string, the component will change its appearance, showing the - error below the date input component. If the defined value is an empty string, it will reserve a space - below the component for a future error, but it would not change its look. In case of being undefined or - null, both the appearance and the space for the error message would not be modified. + false - - - autocomplete + readOnly - string + boolean - HTML autocomplete attribute. Lets the user specify if any permission the user agent has to - provide automated assistance in filling out the input value. Its value must be one of all the possible - values of the HTML autocomplete attribute. See{" "} - MDN{" "} - for further information. + If true, the component will not be mutable, meaning the user can not edit the control. The date picker + cannot be opened either. In addition, the clear action will not be displayed even if the flag is set to + true. - 'off' + false - margin - - 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | Margin - + ref - Size of the margin to be applied to the component. You can pass an object with 'top', 'bottom', 'left' and - 'right' properties in order to specify different margin sizes. + {"React.Ref"} + Reference to the component. - @@ -229,23 +237,15 @@ const sections = [ - ref - - {"React.Ref"} - - Reference to the component. - - - - - ariaLabel + value string - Specifies a string to be used as the name for the date input element when no label is - provided. + Value of the input element. If undefined, the component will be uncontrolled and the value will be managed + internally by the component. - 'Date input' + - diff --git a/apps/website/screens/components/date-input/overview/DateInputOverviewPage.tsx b/apps/website/screens/components/date-input/overview/DateInputOverviewPage.tsx new file mode 100644 index 0000000000..d3a79924fd --- /dev/null +++ b/apps/website/screens/components/date-input/overview/DateInputOverviewPage.tsx @@ -0,0 +1,290 @@ +import { DxcParagraph, DxcBulletedList, DxcFlex } from "@dxc-technology/halstack-react"; +import QuickNavContainer from "@/common/QuickNavContainer"; +import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; +import DocFooter from "@/common/DocFooter"; +import anatomy from "./images/date_input-anatomy.png"; +import clearContent from "./images/date_input-clearcontent.png"; +import datePicker from "./images/date_input-datepicker.png"; +import Image from "@/common/Image"; +import Figure from "@/common/Figure"; + +const sections = [ + { + title: "Introduction", + content: ( + + Date inputs allow users to enter or select a specific date using a calendar picker or manual text entry. + Designed to support a wide range of use cases, from booking systems to form submissions, it ensures clarity and + consistency in date formats, helps prevent input errors, and adapts to different locale and accessibility + requirements. Its combination of manual input and guided selection provides flexibility while maintaining a + streamlined user experience. + + ), + }, + { + title: "Anatomy", + content: ( + <> + Date input's anatomy + + + Label (Optional): a descriptive text that helps users understand what information + is expected in the input field. It should be clear, concise, and placed near the input for better + readability. + + + Optional indicator (Optional): a small indicator that signals the input field is + not mandatory. It helps users know they can leave the field empty without causing validation errors. + + + Date button: an interactive element inside the input field that triggers the date picker of + the component, where the user can select a date based on the day, month and year. + + + Clear action (Optional): a small button, usually represented by an "X" icon, that + allows users to clear the date selected or introduced quickly without manually deleting it. + + + Helper text (Optional): additional text placed below the input field that provides + guidance, examples, or explanations to assist users in filling out the field correctly. + + + Container: the visual wrapper around the input field that provides structure, ensures + accessibility, and helps differentiate the input from other UI elements. + + + Value: displays the selected or manually entered date in the input field, following the + specified format. + + + + ), + }, + { + title: "Form Inputs", + content: ( + <> + + Form inputs are essential UI elements that allow users to interact with digital products by{" "} + entering or selecting data. Choosing the right input type and structure is key to designing + efficient, user-friendly forms that support task completion and data accuracy. + + + A form input (also known as a form field) is used to capture user data. Common input types include text + fields, date pickers, number fields, radio buttons, checkboxes, toggles, and dropdowns. Forms should always + include a submission method, such as a submit button, link, or keyboard trigger, to complete the interaction. + + + ), + subSections: [ + { + title: "Shared input characteristics", + content: ( + <> + + Although input fields vary in type and purpose, they often share a common set of features: + + + + Placeholder: a short hint displayed inside the input field that describes its expected + value or purpose. + + + Helper text: additional information displayed below the field to guide the user in + providing the correct input. + + + Optional label: inputs that are not mandatory can be marked with an "Optional" tag to + set clear expectations. + + + + ), + }, + { + title: "Common input states", + content: ( + <> + Most inputs can also present standard interactive or informative states: + + + Disabled: This state prevents users from interacting with the field. It's typically + used when a value is not applicable or editable under certain conditions or roles. + + + Error: When a user enters invalid or incomplete data, the input shows an error state, + often accompanied by a helpful message to guide corrections. + + + Read-only: The input is visible, focusable, and hoverable, but not editable. This is + ideal for fields with auto-calculated values. Unlike disabled fields, read-only inputs can still be + submitted with the form and are part of the form data. + + + + ), + }, + ], + }, + { + title: "Using date inputs", + content: ( + <> + + Date inputs are designed to help users provide valid, well-formatted dates with minimal friction. Unlike + standard text fields, they combine manual input with an interactive date picker, making them + ideal for scenarios like bookings, forms, or scheduling events. They are particularly useful for reducing + input errors and ensuring consistent formatting across different regions and use cases. + + + ), + subSections: [ + { + title: "Clear action", + content: ( + <> + + As most of our form inputs, the date input includes a clear (close) icon that allows users to quickly + remove the selected or typed date with a single click. This is especially helpful when correcting mistakes + or resetting the field during form completion. The icon is only visible when a value is present, keeping + the interface clean and focused. + +
+ Example of a date input with a clear action +
+ + ), + }, + { + title: "Date picker dialog", + content: ( + <> + + The component features a built-in date picker dialog that can be opened via the calendar icon. This dialog + allows users to select a date visually from a calendar, reducing the likelihood of + formatting errors. The picker includes navigational controls to switch between months and years, ensuring + users can easily access both past and future dates. + +
+ Example of a date picker dialog +
+ + ), + }, + { + title: "Internationalization", + content: ( + + Our date input supports internationalization by adapting to locale-specific formats (e.g., + DD/MM/YYYY vs. MM/DD/YYYY) and calendar language. This ensures that users across different regions + understand the input format and interact with the component comfortably. It's especially important to align + the expected format with the user's regional settings or provide format guidance with placeholders and + helper text. + + ), + }, + ], + }, + { + title: "Best practices", + subSections: [ + { + title: "General", + content: ( + + + Always use the date input when a valid date format is required. This helps ensure consistency and prevents + user error. + + + Display date formats clearly and consistently across your application, especially if users from multiple + locales are expected. + + + Include a clear label that describes the context or purpose of the date (e.g., "Date of birth" or "Start + date"). + + + Avoid setting default dates unless the context explicitly calls for it, such as pre-filling today's date + for quick scheduling. + + + ), + }, + { + title: "Formatting and validation", + content: ( + + + Match the date format to the user’s regional settings (DD/MM/YYYY or MM/DD/YYYY), and consider using + placeholder text to guide formatting. + + + Provide clear feedback if the user types an invalid date manually. + + + Avoid using text inputs with custom formatting masks in place of the date input component—this can confuse + users and complicate validation. + + + ), + }, + { + title: "Clear action", + content: ( + + + Use the clear (close) icon to let users easily remove an already selected date. This improves usability + for forms where the date might not be required. + + + Ensure the clear icon is only visible when a value is present, keeping the interface clean. + + + ), + }, + { + title: "Date picker dialog", + content: ( + + + Include the date picker to reduce formatting errors and speed up date selection, especially for less + tech-savvy users or on mobile. + + + For workflows that involve selecting a wide range of dates (e.g., historical records), make sure the date + picker supports quick navigation between months and years. + + + ), + }, + { + title: "Accessibility and internationalization", + content: ( + + + Support multiple locales to accommodate different date formats and calendar languages, and update labels + accordingly. + + + Use ISO 8601 format internally (YYYY-MM-DD) to prevent errors during submission or back-end processing. + + + ), + }, + ], + }, +]; + +const DateInputOverviewPage = () => { + return ( + + + + + + + ); +}; + +export default DateInputOverviewPage; diff --git a/apps/website/screens/components/date-input/overview/images/date_input-anatomy.png b/apps/website/screens/components/date-input/overview/images/date_input-anatomy.png new file mode 100644 index 0000000000..ef421a65e7 Binary files /dev/null and b/apps/website/screens/components/date-input/overview/images/date_input-anatomy.png differ diff --git a/apps/website/screens/components/date-input/overview/images/date_input-clearcontent.png b/apps/website/screens/components/date-input/overview/images/date_input-clearcontent.png new file mode 100644 index 0000000000..7391ca16de Binary files /dev/null and b/apps/website/screens/components/date-input/overview/images/date_input-clearcontent.png differ diff --git a/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png b/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png new file mode 100644 index 0000000000..d618ee1d5e Binary files /dev/null and b/apps/website/screens/components/date-input/overview/images/date_input-datepicker.png differ diff --git a/apps/website/screens/components/date-input/specs/DateInputSpecsPage.tsx b/apps/website/screens/components/date-input/specs/DateInputSpecsPage.tsx deleted file mode 100644 index 902509e233..0000000000 --- a/apps/website/screens/components/date-input/specs/DateInputSpecsPage.tsx +++ /dev/null @@ -1,525 +0,0 @@ -import { DxcParagraph, DxcBulletedList, DxcTable, DxcFlex, DxcLink } from "@dxc-technology/halstack-react"; -import Image from "@/common/Image"; -import Link from "next/link"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import Figure from "@/common/Figure"; -import Code from "@/common/Code"; -import dateSpecsStates from "./images/input_date_states.png"; -import dateSpecsPopup from "./images/date_popup.png"; -import dateSpecsAnatomy from "./images/input_date_anatomy.png"; -import dateSpecs from "./images/input_date_specs.png"; -import datePopupSpecs from "./images/date_popup_specs.png"; - -const sections = [ - { - title: "Date input", - content: ( - <> -
- Date input design specifications -
- - The date input color, typography, border, spacing,{" "} - width and margin specifications are inherited from the text input, for reference - check{" "} - - text input - {" "} - documentation . - - - The date input doesn't have the following text input elements or properties, therefore, their listed - styles don't apply: - - - Prefix / Suffix - - The size small is not available - - - - ), - }, - { - title: "Date picker dialog", - content: ( -
- Date picker design specifications -
- ), - }, - { - title: "States", - subSections: [ - { - title: "Date input", - content: ( - <> - - States: enabled, hover, focus, error, - and disabled. - -
- Date input states -
- - ), - }, - { - title: "Date picker dialog", - content: ( - <> - - States: default, hover, focus, active,{" "} - selected, today and disabled. - -
- Calendar day states -
- - ), - }, - ], - }, - { - title: "Anatomy", - content: ( - <> - Date input anatomy - - Label - - Helper text (Optional) - - Input container - Date button - Clear action - Error indicator - Error message - Placeholder / value - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - pickerBackgroundColor - - Date picker container - - color-white - - #ffffff - - - - pickerFontColor - - Date picker container - - color-black - - #000000 - - - - pickerBorderColor - - Date picker container - - color-grey-400 - - #bfbfbf - - - - pickerSelectedBackgroundColor - - Picker date/year: selected - - color-purple-700 - - #5f249f - - - - pickerSelectedFontColor - - Picker date/year: selected - - color-white - - #ffffff - - - - pickerHoverBackgroundColor - - Picker date/year: hover - - color-purple-200 - - #e5d5f6 - - - - pickerHoverFontColor - - Picker date/year: active - - color-black - - #000000 - - - - pickerActiveBackgroundColor - - Picker date/year: active - - color-purple-800 - - #4b1c7d - - - - pickerActiveFontColor - - Picker date/year: active - - color-white - - #ffffff - - - - pickerNonCurrentMonthFontColor - - Picker date: other month - - color-grey-500 - - #999999 - - - - pickerCurrentDateBorderColor - - Picker date/year: current - - color-purple-300 - - #cbacec - - - - pickerCurrentDateFontColor - - Picker date: current - - color-black - - #000000 - - - - pickerCurrentYearFontColor - - Picker year: current - - color-purple-700 - - #5f249f - - - - pickerHeaderBackgroundColor - - Picker header - - color-transparent - - transparent - - - - pickerHeaderFontColor - - Picker header - - color-black - - #000000 - - - - pickerHeaderHoverBackgroundColor - - Picker header: hover - - color-purple-200 - - #e5d5f6 - - - - pickerHeaderHoverFontColor - - Picker header: hover - - color-black - - #000000 - - - - pickerHeaderActiveBackgroundColor - - Picker header: active - - color-purple-800 - - #4b1c7d - - - - pickerHeaderActiveFontColor - - Picker header: active - - color-white - - #ffffff - - - - pickerFocusColor - - Date picker container - - color-blue-600 - - #0095ff - - - - ), - }, - { - title: "Border", - content: ( - - - - Component token - Element - Core token - Value - - - - - - pickerBorderWidth - - Date picker container - - border-width-1 - - 1px - - - - pickerBorderStyle - - Date picker container - - border-style-solid - - solid - - - - pickerFocusWidth - - Date picker container: focus - - border-width-2 - - 2px - - - - pickerCurrentDateBorderWidth - - Picker date/year: current - - border-width-1 - - 1px - - - - ), - }, - { - title: "Typography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - pickerFontFamily - - Date picker container - - font-family-sans - - 'Open Sans', sans-serif - - - - pickerFontSize - - Date picker container - - font-scale-02 - - 0.875rem - - - - pickerFontWeight - - Date picker container - - font-regular - - 400 - - - - pickerInteractedYearFontSize - - Picker year: selected, hover, active - - font-scale-05 - - 1.5rem - - - - pickerHeaderFontSize - - Picker header - - font-scale-02 - - 0.875rem - - - - ), - }, - ], - }, - { - title: "Accessibility", - subSections: [ - { - title: "WCAG 2.2", - content: ( - <> - - - Understanding WCAG 2.2 -{" "} - - SC 2.1.1: Keyboard - - - - Understanding WCAG 2.2 -{" "} - - SC 2.1.2: No keyboard trap - - - - Understanding WCAG 2.2 -{" "} - - SC 2.4.6: Headings and labels - - - - Understanding WCAG 2.2 -{" "} - - SC 2.4.7: Focus Visible - - - - - ), - }, - { - title: "WAI-ARIA 1.2", - content: ( - - - WAI-ARIA authoring practices 1.2 -{" "} - - 3.9 Dialog modal - - - - WAI-ARIA authoring practices 1.2 -{" "} - - Date picker design pattern - - - - ), - }, - ], - }, -]; - -const DateInputSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default DateInputSpecsPage; diff --git a/apps/website/screens/components/date-input/specs/images/date_popup.png b/apps/website/screens/components/date-input/specs/images/date_popup.png deleted file mode 100644 index 9142dac930..0000000000 Binary files a/apps/website/screens/components/date-input/specs/images/date_popup.png and /dev/null differ diff --git a/apps/website/screens/components/date-input/specs/images/date_popup_specs.png b/apps/website/screens/components/date-input/specs/images/date_popup_specs.png deleted file mode 100644 index 3d14a79060..0000000000 Binary files a/apps/website/screens/components/date-input/specs/images/date_popup_specs.png and /dev/null differ diff --git a/apps/website/screens/components/date-input/specs/images/input_date_anatomy.png b/apps/website/screens/components/date-input/specs/images/input_date_anatomy.png deleted file mode 100644 index 223fd62ba0..0000000000 Binary files a/apps/website/screens/components/date-input/specs/images/input_date_anatomy.png and /dev/null differ diff --git a/apps/website/screens/components/date-input/specs/images/input_date_specs.png b/apps/website/screens/components/date-input/specs/images/input_date_specs.png deleted file mode 100644 index a4933aae7a..0000000000 Binary files a/apps/website/screens/components/date-input/specs/images/input_date_specs.png and /dev/null differ diff --git a/apps/website/screens/components/date-input/specs/images/input_date_states.png b/apps/website/screens/components/date-input/specs/images/input_date_states.png deleted file mode 100644 index f7c4d36d33..0000000000 Binary files a/apps/website/screens/components/date-input/specs/images/input_date_states.png and /dev/null differ diff --git a/apps/website/screens/components/date-input/usage/DateInputUsagePage.tsx b/apps/website/screens/components/date-input/usage/DateInputUsagePage.tsx deleted file mode 100644 index 2092df5cc2..0000000000 --- a/apps/website/screens/components/date-input/usage/DateInputUsagePage.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { DxcParagraph, DxcBulletedList, DxcFlex } from "@dxc-technology/halstack-react"; -import QuickNavContainer from "@/common/QuickNavContainer"; -import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; -import DocFooter from "@/common/DocFooter"; -import Code from "@/common/Code"; - -const sections = [ - { - title: "Usage", - content: ( - - - Use the date input component when asking for a past, present, or future date. - - Provide a hint with the date format expected in the placeholder. - Use a concise label to indicate what the date selection refers to. - - ), - }, - - { - title: "Internationalization", - content: ( - <> - - The date format depends on an ISO definition for each country and the preference of use within the - application. - - - The format of the date may vary depending on language, region, country or customer. It is a good practice to - give to the user some type of hint about the date format and in many cases. - - - - The default format for the United States is mm/dd/yyyy. - - - The default format for Australia, Europe, Africa, South America most countries of Asia is{" "} - dd/mm/yyyy. - - - The default format in China is yyyy/mm/dd. - - - - ), - }, -]; - -const DateInputUsagePage = () => { - return ( - - - - - - - ); -}; - -export default DateInputUsagePage; diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index ae2aacdc2d..2c701f2d94 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -4,6 +4,93 @@ import styled from "styled-components"; import { CalendarPropsType, DateType } from "./types"; import { HalstackLanguageContext } from "../HalstackContext"; +const CalendarContainer = styled.div` + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + width: 292px; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + color: var(--color-fg-neutral-dark); + font-weight: var(--typography-label-regular); +`; + +const CalendarHeaderRow = styled.div` + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + align-items: center; +`; + +const WeekHeaderCell = styled.span` + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: var(--height-m); +`; + +const MonthContainer = styled.div` + box-sizing: border-box; + display: flex; + gap: var(--spacing-gap-xs); + flex-direction: column; + justify-content: space-between; +`; + +const WeekContainer = styled.div` + box-sizing: border-box; + display: flex; + gap: var(--spacing-gap-xs); + justify-content: space-between; +`; + +const DayCellButton = styled.button<{ + selected: boolean; + actualMonth: boolean; + isCurrentDay: boolean; +}>` + display: inline-flex; + justify-content: center; + align-items: center; + width: 32px; + height: var(--height-m); + padding: 0; + border: none; + border-radius: var(--border-radius-xl); + cursor: pointer; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-regular, 400); + background-color: ${(props) => (props.selected ? "var(--color-bg-primary-strong);" : "transparent")}; + color: ${(props) => + props.selected + ? "var(--color-fg-neutral-bright);" + : !props.actualMonth + ? "var(--color-fg-neutral-medium);" + : "var(--color-fg-neutral-dark);"}; + + ${(props) => + props.isCurrentDay && + !props.selected && + `border: var(--border-width-s) var(--border-style-default) var(--border-color-primary-lighter);`} + + &:focus { + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + } + &:hover { + background-color: ${(props) => + props.selected ? "var(--color-bg-primary-strong);" : "var(--color-bg-primary-lighter);"}; + color: ${(props) => (props.selected ? "var(--color-fg-neutral-bright);" : "var(--color-fg-neutral-dark);")}; + } + &:active { + background-color: var(--color-bg-primary-stronger); + color: var(--color-fg-neutral-bright); + } +`; + const getDays = (innerDate: Dayjs) => { const monthDayCells: DateType[] = []; const lastMonthNumberOfDays = innerDate.set("month", innerDate.get("month") - 1).endOf("month"); @@ -207,99 +294,4 @@ const Calendar = ({ ); }; -const CalendarContainer = styled.div` - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; - padding: 0px 8px 8px 8px; - width: 292px; - font-family: ${(props) => props.theme.dateInput.pickerFontFamily}; - font-size: ${(props) => props.theme.dateInput.pickerFontSize}; - color: ${(props) => props.theme.dateInput.pickerFontColor}; - font-weight: ${(props) => props.theme.dateInput.pickerFontWeight}; -`; - -const CalendarHeaderRow = styled.div` - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; - align-items: center; -`; - -const WeekHeaderCell = styled.span` - display: flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; -`; - -const MonthContainer = styled.div` - box-sizing: border-box; - display: flex; - gap: 4px; - flex-direction: column; - justify-content: space-between; -`; - -const WeekContainer = styled.div` - box-sizing: border-box; - display: flex; - gap: 4px; - justify-content: space-between; -`; - -const DayCellButton = styled.button<{ - selected: boolean; - actualMonth: boolean; - isCurrentDay: boolean; -}>` - display: inline-flex; - justify-content: center; - align-items: center; - width: 36px; - height: 36px; - padding: 0; - border: none; - border-radius: 50%; - cursor: pointer; - font-family: ${(props) => props.theme.dateInput.pickerFontFamily}; - font-size: ${(props) => props.theme.dateInput.pickerFontSize}; - color: ${(props) => props.theme.dateInput.pickerFontColor}; - font-weight: ${(props) => props.theme.dateInput.pickerFontWeight}; - - &:focus { - outline: ${(props) => props.theme.dateInput.pickerFocusColor} solid 2px; - } - &:hover { - background-color: ${(props) => - props.selected - ? props.theme.dateInput.pickerSelectedBackgroundColor - : props.theme.dateInput.pickerHoverBackgroundColor}; - color: ${(props) => - props.selected ? props.theme.dateInput.pickerSelectedFontColor : props.theme.dateInput.pickerHoverFontColor}; - } - &:active { - background-color: ${(props) => props.theme.dateInput.pickerActiveBackgroundColor}; - color: ${(props) => props.theme.dateInput.pickerActiveFontColor}; - } - - ${(props) => - props.isCurrentDay && - !props.selected && - `border: ${props.theme.dateInput.pickerCurrentDateBorderWidth} solid ${props.theme.dateInput.pickerCurrentDateBorderColor};`} - background-color: ${(props) => - props.selected ? props.theme.dateInput.pickerSelectedBackgroundColor : "transparent"}; - color: ${(props) => - props.selected - ? props.theme.dateInput.pickerSelectedFontColor - : props.isCurrentDay - ? props.theme.dateInput.pickerCurrentDateFontColor - : !props.actualMonth - ? props.theme.dateInput.pickerNonCurrentMonthFontColor - : props.theme.dateInput.pickerFontColor}; -`; - export default memo(Calendar); diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 49e00a94bc..7058edd15a 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -26,6 +26,67 @@ dayjs.extend(customParseFormat); const SIDEOFFSET = 4; +const sizes = { + small: "240px", + medium: "360px", + large: "480px", + fillParent: "100%", +}; + +const calculateWidth = (margin: DateInputPropsType["margin"], size: DateInputPropsType["size"]) => + size === "fillParent" + ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` + : size && sizes[size]; + +const DateInputContainer = styled.div<{ margin: DateInputPropsType["margin"]; size: DateInputPropsType["size"] }>` + ${(props) => props.size === "fillParent" && "width: 100%;"} + display: flex; + flex-direction: column; + width: ${(props) => calculateWidth(props.margin, props.size)}; + ${(props) => props.size !== "fillParent" && `min-width:${calculateWidth(props.margin, props.size)}`}; + 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] : ""}; + font-family: var(--typography-font-family); +`; + +const Label = styled.label<{ + disabled: DateInputPropsType["disabled"]; + hasHelperText: boolean; +}>` + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium);" : "var(--color-fg-neutral-dark);")}; + font-size: var(--typography-label-m); + font-weight: var(--typography-label-semibold); + ${(props) => !props.hasHelperText && "margin-bottom: var(--spacing-gap-xs);"} +`; + +const OptionalLabel = styled.span<{ + disabled: DateInputPropsType["disabled"]; +}>` + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium);" : "var(--color-fg-neutral-stronger);")}; + font-weight: var(--typography-label-regular); +`; + +const HelperText = styled.span<{ disabled: DateInputPropsType["disabled"] }>` + color: ${(props) => (props.disabled ? "var(--color-fg-neutral-medium);" : "var(--color-fg-neutral-stronger);")}; + font-size: var(--typography-helper-text-s); + font-weight: var(--typography-helper-text-regular); + margin-bottom: var(--spacing-gap-xs); +`; + +const StyledPopoverContent = styled(Popover.Content)` + z-index: 2147483647; + &:focus-visible { + outline: none; + } +`; + const getValueForPicker = (value: string, format: string) => dayjs(value, format.toUpperCase(), true); const getDate = ( @@ -234,7 +295,10 @@ const DxcDateInput = forwardRef( disabled={disabled} hasHelperText={!!helperText} > - {label} {optional && {translatedLabels.formFields.optionalLabel}} + {label}{" "} + {optional && ( + {translatedLabels.formFields.optionalLabel} + )} )} {helperText && {helperText}} @@ -283,68 +347,4 @@ const DxcDateInput = forwardRef( } ); -const sizes = { - small: "240px", - medium: "360px", - large: "480px", - fillParent: "100%", -}; - -const calculateWidth = (margin: DateInputPropsType["margin"], size: DateInputPropsType["size"]) => - size === "fillParent" - ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : size && sizes[size]; - -const DateInputContainer = styled.div<{ margin: DateInputPropsType["margin"]; size: DateInputPropsType["size"] }>` - ${(props) => props.size === "fillParent" && "width: 100%;"} - display: flex; - flex-direction: column; - width: ${(props) => calculateWidth(props.margin, props.size)}; - ${(props) => props.size !== "fillParent" && `min-width:${calculateWidth(props.margin, props.size)}`}; - 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] : ""}; - font-family: ${(props) => props.theme.textInput.fontFamily}; -`; - -const Label = styled.label<{ - disabled: DateInputPropsType["disabled"]; - hasHelperText: boolean; -}>` - color: ${(props) => - props.disabled ? props.theme.textInput.disabledLabelFontColor : props.theme.textInput.labelFontColor}; - font-size: ${(props) => props.theme.textInput.labelFontSize}; - font-style: ${(props) => props.theme.textInput.labelFontStyle}; - font-weight: ${(props) => props.theme.textInput.labelFontWeight}; - line-height: ${(props) => props.theme.textInput.labelLineHeight}; - ${(props) => !props.hasHelperText && `margin-bottom: 0.25rem`} -`; - -const OptionalLabel = styled.span` - font-weight: ${(props) => props.theme.textInput.optionalLabelFontWeight}; -`; - -const HelperText = styled.span<{ disabled: DateInputPropsType["disabled"] }>` - color: ${(props) => - props.disabled ? props.theme.textInput.disabledHelperTextFontColor : props.theme.textInput.helperTextFontColor}; - font-size: ${(props) => props.theme.textInput.helperTextFontSize}; - font-style: ${(props) => props.theme.textInput.helperTextFontStyle}; - font-weight: ${(props) => props.theme.textInput.helperTextFontWeight}; - line-height: ${(props) => props.theme.textInput.helperTextLineHeight}; - margin-bottom: 0.25rem; -`; - -const StyledPopoverContent = styled(Popover.Content)` - z-index: 2147483647; - &:focus-visible { - outline: none; - } -`; - export default DxcDateInput; diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx index a8716333bf..8ec363830e 100644 --- a/packages/lib/src/date-input/DatePicker.tsx +++ b/packages/lib/src/date-input/DatePicker.tsx @@ -8,6 +8,77 @@ import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; import { HalstackLanguageContext } from "../HalstackContext"; +const DatePickerContainer = styled.div` + padding: var(--spacing-padding-m) var(--spacing-padding-xs) var(--spacing-padding-xs) var(--spacing-padding-xs); + background-color: var(--color-bg-neutral-lightest); + box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) + var(--shadow-light); + border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-medium); + border-radius: var(--border-radius-s); + width: fit-content; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + color: var(--color-fg-neutral-dark); + font-weight: var(--typography-label-regular); + display: flex; + flex-direction: column; + gap: var(--spacing-gap-xxs); +`; + +const PickerHeader = styled.div` + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: space-between; + height: var(--height-m); +`; + +const HeaderButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: var(--height-s); + padding: 0px; + color: var(--color-fg-neutral-dark); + background-color: var(--color-bg-neutral-lightest); + border-radius: var(--border-radius-s); + border: none; + cursor: pointer; + + &:hover { + background-color: var(--color-bg-primary-light); + } + &:focus { + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + } + &:active { + color: var(--color-fg-neutral-bright); + background-color: var(--color-bg-primary-stronger); + } + + span::before { + font-size: var(--height-s); + } +`; + +const HeaderYearTrigger = styled(HeaderButton)` + gap: var(--spacing-gap-s); + padding: 0px var(--spacing-padding-xs) 0px var(--spacing-padding-m); + height: var(--height-m); + width: 172px; + span::before { + font-size: var(--height-xxs); + } +`; + +const HeaderYearTriggerLabel = styled.span` + display: flex; + align-items: center; + justify-content: center; + font-size: var(--typography-label-m); +`; + const today = dayjs(); const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Element => { @@ -75,76 +146,4 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen ); }; -const DatePickerContainer = styled.div` - padding-top: 16px; - background-color: ${(props) => props.theme.dateInput.pickerBackgroundColor}; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); - border: ${(props) => `${props.theme.dateInput.pickerBorderWidth} ${props.theme.dateInput.pickerBorderStyle} - ${props.theme.dateInput.pickerBorderColor}`}; - border-radius: 4px; - width: fit-content; - font-family: ${(props) => props.theme.dateInput.pickerFontFamily}; - font-size: ${(props) => props.theme.dateInput.pickerFontSize}; - color: ${(props) => props.theme.dateInput.pickerFontColor}; - font-weight: ${(props) => props.theme.dateInput.pickerFontWeight}; -`; - -const PickerHeader = styled.div` - box-sizing: border-box; - display: flex; - gap: 8px; - align-items: center; - justify-content: space-between; - padding: 0px 16px; -`; - -const HeaderButton = styled.button` - display: flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - padding: 0px; - color: ${(props) => props.theme.dateInput.pickerHeaderFontColor}; - background-color: ${(props) => props.theme.dateInput.pickerHeaderBackgroundColor}; - border-radius: 4px; - border: none; - cursor: pointer; - - &:hover { - color: ${(props) => props.theme.dateInput.pickerHeaderHoverFontColor}; - background-color: ${(props) => props.theme.dateInput.pickerHeaderHoverBackgroundColor}; - } - &:focus { - outline: ${(props) => `${props.theme.dateInput.pickerFocusColor} solid - ${props.theme.dateInput.pickerFocusWidth}`}; - } - &:active { - color: ${(props) => props.theme.dateInput.pickerHeaderActiveFontColor}; - background-color: ${(props) => props.theme.dateInput.pickerHeaderActiveBackgroundColor}; - } - - span::before { - font-size: 24px; - } -`; - -const HeaderYearTrigger = styled(HeaderButton)` - gap: 8px; - height: 40px; - width: 172px; - font-size: 24px; - span::before { - font-size: 24px; - } -`; - -const HeaderYearTriggerLabel = styled.span` - display: flex; - align-items: center; - justify-content: center; - font-family: ${(props) => props.theme.dateInput.pickerFontFamily}; - font-size: ${(props) => props.theme.dateInput.pickerHeaderFontSize}; -`; - export default memo(DatePicker); diff --git a/packages/lib/src/date-input/YearPicker.tsx b/packages/lib/src/date-input/YearPicker.tsx index 7b8324cbf8..15cc90e9c6 100644 --- a/packages/lib/src/date-input/YearPicker.tsx +++ b/packages/lib/src/date-input/YearPicker.tsx @@ -3,6 +3,64 @@ import { useEffect, useId, useState, memo, KeyboardEvent } from "react"; import styled from "styled-components"; import { YearPickerPropsType } from "./types"; +const YearPickerContainer = styled.div` + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: var(--spacing-gap-xs); + align-items: center; + overflow-y: scroll; + width: 292px; + height: 312px; + box-shadow: var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) + var(--shadow-light); +`; + +const YearPickerButton = styled.button<{ + selected: boolean; + isCurrentYear: boolean; +}>` + display: flex; + align-items: center; + justify-content: center; + width: 80px; + min-height: var(--height-m); + height: var(--height-m); + background-color: transparent; + border: none; + border-radius: var(--border-radius-xl); + cursor: pointer; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + color: var(--color-fg-neutral-dark); + font-weight: var(--typography-label-regular); + + ${(props) => + props.selected + ? `font-size: var(--typography-label-xl); + color: var(--color-fg-neutral-bright) !important; + background-color: var(--color-bg-primary-strong) !important;` + : props.isCurrentYear + ? `border: var(--border-width-s) var(--border-style-default) var(--border-color-primary-lighter); + color: var(--color-fg-primary-strong);` + : ``} + + &:hover, &:focus, &:active { + font-size: var(--typography-label-xl); + } + &:hover { + background-color: var(--color-bg-primary-light); + color: var(--color-fg-neutral-dark); + } + &:focus { + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + } + &:active { + color: var(--color-fg-neutral-bright); + background-color: var(--color-bg-primary-stronger); + } +`; + const getYearsArray = () => { const yearList = []; for (let i = 1899; i <= 2100; i++) { @@ -10,6 +68,7 @@ const getYearsArray = () => { } return yearList; }; + const yearList = getYearsArray(); const YearPicker = ({ onYearSelect, selectedDate, today }: YearPickerPropsType): JSX.Element => { @@ -59,65 +118,4 @@ const YearPicker = ({ onYearSelect, selectedDate, today }: YearPickerPropsType): ); }; -const YearPickerContainer = styled.div` - box-sizing: border-box; - display: flex; - flex-direction: column; - gap: 4px; - align-items: center; - overflow-y: scroll; - width: 292px; - height: 312px; - padding: 2px 8px 8px 8px; -`; - -const YearPickerButton = styled.button<{ - selected: boolean; - isCurrentYear: boolean; -}>` - display: flex; - align-items: center; - justify-content: center; - width: 80px; - min-height: 40px; - line-height: 21px; - background-color: transparent; - border: none; - border-radius: 50px; - cursor: pointer; - font-family: ${(props) => props.theme.dateInput.pickerFontFamily}; - font-size: ${(props) => props.theme.dateInput.pickerFontSize}; - color: ${(props) => props.theme.dateInput.pickerFontColor}; - font-weight: ${(props) => props.theme.dateInput.pickerFontWeight}; - - ${(props) => - props.selected - ? `font-size: ${props.theme.dateInput.pickerInteractedYearFontSize}; - line-height: 36px; - color: ${props.theme.dateInput.pickerSelectedFontColor} !important; - background-color: ${props.theme.dateInput.pickerSelectedBackgroundColor} !important;` - : props.isCurrentYear - ? `border: 1px solid ${props.theme.dateInput.pickerCurrentDateBorderColor}; - color: ${props.theme.dateInput.pickerCurrentYearFontColor};` - : ``} - - &:hover, &:focus, &:active { - font-size: ${(props) => props.theme.dateInput.pickerInteractedYearFontSize}; - line-height: 36px; - } - &:hover { - color: ${(props) => props.theme.dateInput.pickerHoverFontColor}; - background-color: ${(props) => props.theme.dateInput.pickerHoverBackgroundColor}; - } - &:focus { - color: ${(props) => props.theme.dateInput.pickerHoverFontColor}; - outline: ${(props) => `${props.theme.dateInput.pickerFocusColor} solid - ${props.theme.dateInput.pickerFocusWidth}`}; - } - &:active { - color: ${(props) => props.theme.dateInput.pickerActiveFontColor}; - background-color: ${(props) => props.theme.dateInput.pickerActiveBackgroundColor} !important; - } -`; - export default memo(YearPicker);