From ba94b8028f15c0491046a9860f5b5de541b1afc1 Mon Sep 17 00:00:00 2001 From: yplarthur Date: Tue, 13 Feb 2024 11:10:15 +0100 Subject: [PATCH 01/64] =?UTF-8?q?=E2=9C=A8=20UPCO-211=20-=20Add=20qa=20att?= =?UTF-8?q?ribute=20to=20DefinitionList=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ .../react-components/src/lib/atoms/list/List.types.ts | 2 +- .../lib/atoms/list/definition-list/DefinitionList.stories.jsx | 4 ++-- .../src/lib/atoms/list/definition-list/DefinitionList.tsx | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f74beb4e..635ceb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upload component: A successfully uploaded no longer has a `danger` themed delete button. +### Added + +- Definition list items can have a `qa` attribute. + ## 7.0.7 ### Fixed diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts b/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts index b1bdb3c7..74ed442b 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts @@ -3,7 +3,7 @@ import { AvatarProps } from '../avatar'; import { CheckboxProps } from '../checkbox'; export interface DefinitionListProps { - items: { id: string; term: string; description: string }[]; + items: { id: string; term: string; description: string; qa?: string }[]; qa?: string; } diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx index acd89c21..46b44a21 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx @@ -17,7 +17,7 @@ export default { args: { items: [ { id: '1', term: 'Hello', description: 'World' }, - { id: '2', term: 'Term', description: 'Term description' } + { id: '2', term: 'Term', description: 'Term description', qa: 'data-qa value' } ] }, argTypes: { @@ -28,7 +28,7 @@ export default { defaultValue: { summary: [] } }, description: - 'Items of the definition list. Array of objects `{id: "string", term: "string", description: "string"}`.' + 'Items of the definition list. Array of objects `{id: "string", term: "string", description: "string", "qa": "string"}`.' }, qa: QA_PROP_STORY } diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx index d688baa4..f0e07db6 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx @@ -5,7 +5,7 @@ export function DefinitionList({ items, qa }: DefinitionListProps) { return (
{items.map((i) => ( - +
{i.term}
{i.description}
From 2c6ae114552f28b9dd6fc78c39de81e2ff369f8b Mon Sep 17 00:00:00 2001 From: Nicolas T Date: Thu, 15 Feb 2024 13:12:24 +0100 Subject: [PATCH 02/64] Update changelog --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d450234..208346db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -### Changed - -- Replaced defaultProps with default arguments. - ### Fixed - Upload component: A successfully uploaded no longer has a `danger` themed delete button. +- Replaced defaultProps with default arguments (defaultProps are deprecated in React 18). ## 7.0.7 From c2e335ffed433f792a1ac299f2efe99827c5771e Mon Sep 17 00:00:00 2001 From: yplarthur Date: Thu, 15 Feb 2024 13:41:26 +0100 Subject: [PATCH 03/64] =?UTF-8?q?=F0=9F=90=9B=20UPCO-211=20-=20Replace=20q?= =?UTF-8?q?a=20prop=20in=20definition=20list=20item=20with=20qaTerm=20and?= =?UTF-8?q?=20qaDescription?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- documentation/playground/src/atoms/ListExamples.tsx | 2 +- .../react-components/src/lib/atoms/list/List.types.ts | 10 +++++++++- .../list/definition-list/DefinitionList.stories.jsx | 10 ++++++++-- .../lib/atoms/list/definition-list/DefinitionList.tsx | 6 +++--- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 635ceb7f..57e08a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Definition list items can have a `qa` attribute. +- Definition list items can have a `qaTerm` and `qaDescription` properties for use as data-qa attributes on the respective terms and descriptions. ## 7.0.7 diff --git a/documentation/playground/src/atoms/ListExamples.tsx b/documentation/playground/src/atoms/ListExamples.tsx index 41f3f091..779ca8f7 100644 --- a/documentation/playground/src/atoms/ListExamples.tsx +++ b/documentation/playground/src/atoms/ListExamples.tsx @@ -17,7 +17,7 @@ export function ListExamples() {

Lists

Definition

- +

Functional

Clicked on {nameClicked}

diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts b/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts index 74ed442b..0324a9fa 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/List.types.ts @@ -3,10 +3,18 @@ import { AvatarProps } from '../avatar'; import { CheckboxProps } from '../checkbox'; export interface DefinitionListProps { - items: { id: string; term: string; description: string; qa?: string }[]; + items: DefinitionListItemProps[]; qa?: string; } +export interface DefinitionListItemProps { + id: string; + term: string; + description: string; + qaTerm?: string; + qaDescription?: string; +} + export interface FunctionalListProps { type?: 'avatar' | 'checkbox'; flushed?: boolean; diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx index 46b44a21..ce94de55 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.stories.jsx @@ -17,7 +17,13 @@ export default { args: { items: [ { id: '1', term: 'Hello', description: 'World' }, - { id: '2', term: 'Term', description: 'Term description', qa: 'data-qa value' } + { + id: '2', + term: 'Term', + description: 'Term description', + qaTerm: 'data-qa value on term', + qaDescription: 'data-qa value on description' + } ] }, argTypes: { @@ -28,7 +34,7 @@ export default { defaultValue: { summary: [] } }, description: - 'Items of the definition list. Array of objects `{id: "string", term: "string", description: "string", "qa": "string"}`.' + 'Items of the definition list. Array of objects `{id: "string", term: "string", description: "string", "qaTerm": "string", "qaDescription": "string"}` To add a data-qa attribute to the term or description, add a `qaTerm` or `qaDescription` property to the object.' }, qa: QA_PROP_STORY } diff --git a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx index f0e07db6..e9329536 100644 --- a/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx +++ b/packages/antwerp-ui/react-components/src/lib/atoms/list/definition-list/DefinitionList.tsx @@ -5,9 +5,9 @@ export function DefinitionList({ items, qa }: DefinitionListProps) { return (
{items.map((i) => ( - -
{i.term}
-
{i.description}
+ +
{i.term}
+
{i.description}
))}
From 45c9c187149d640d336802794689160de67cb2b9 Mon Sep 17 00:00:00 2001 From: Nicolas T Date: Mon, 19 Feb 2024 15:08:08 +0100 Subject: [PATCH 04/64] Version 7.1.0 Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23220f29..74cd5ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -Version 7.0.7 +Version 7.1.0 # Changelog @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 7.1.0 - 2024-02-19 ### Fixed From 236ddf70ae20930ea1e6762c9b20481128c10265 Mon Sep 17 00:00:00 2001 From: Nicolas T Date: Fri, 23 Feb 2024 10:10:34 +0100 Subject: [PATCH 05/64] UPCO-214 --- CHANGELOG.md | 6 ++++ .../src/molecules/DatepickerExamples.tsx | 18 ++++++++++- .../molecules/datepicker/Datepicker.spec.tsx | 8 +++++ .../lib/molecules/datepicker/Datepicker.tsx | 31 ++++++++++++++----- .../molecules/datepicker/Datepicker.types.ts | 2 +- 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74cd5ca9..b808b3ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Allow an onChange listener on the input field of the Datepicker. + ## 7.1.0 - 2024-02-19 ### Fixed diff --git a/documentation/playground/src/molecules/DatepickerExamples.tsx b/documentation/playground/src/molecules/DatepickerExamples.tsx index 47ea759a..8ad6139f 100644 --- a/documentation/playground/src/molecules/DatepickerExamples.tsx +++ b/documentation/playground/src/molecules/DatepickerExamples.tsx @@ -5,6 +5,7 @@ import { useState } from 'react'; export function DatepickerExamples() { const [date, setDate] = useState(new Date('2023-02-22').toISOString()); + const [customError, setCustomError] = useState('' as string); return (
@@ -45,7 +46,22 @@ export function DatepickerExamples() { /> + { + return setCustomError(`The wrong date has "${e.target.value.length}" characters`); + } + }} + calendarProps={{ unavailableFrom: new Date('2024-02-24').toISOString() }} format="dd-MM-yyyy" value={new Date(Date.now()).toISOString()} /> diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx index 07cf26d1..b5273c08 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx @@ -158,4 +158,12 @@ describe('UI Components - Molecules - Datepicker', () => { const { baseElement } = render(); expect(baseElement.querySelector('is-clickable')).not.toBeTruthy(); }); + + it('should allow to define an onChange listener on input props', () => { + const mockOnChange = jest.fn(); + const { baseElement } = render(); + const input = baseElement.querySelector('#aui-text-field') as HTMLInputElement; + fireEvent.change(input, { target: { value: 'invalid' } }); + expect(mockOnChange).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.tsx b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.tsx index fdaa6f34..eb0a9772 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.tsx +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.tsx @@ -1,13 +1,13 @@ import { DatepickerProps } from './Datepicker.types'; import { DEFAULT_DATE_FORMAT } from '../../../constants/settings'; -import { formatISO } from 'date-fns'; +import { formatISO, set } from 'date-fns'; import { Icon } from '../../base/icon'; import { isValid as fnsIsValid, format as fnsFormat, parse as fnsParse } from 'date-fns'; import { renderDescription, renderLabel } from '../../atoms/input/input.renders'; import { TextField } from '../../atoms/input'; import { useOutsideClick } from '../../../utils/custom.hooks'; import Calendar from './Calendar'; -import React, { FocusEvent, KeyboardEvent, useRef, useState } from 'react'; +import React, { FocusEvent, KeyboardEvent, useEffect, useRef, useState } from 'react'; import { isInRange } from '../../../utils/time.utils'; export function Datepicker({ @@ -28,6 +28,11 @@ export function Datepicker({ const [dateInvalidError, setDateInvalidError] = useState(''); const [isOpen, setIsOpen] = useState(false); + useEffect(() => { + setErrorMessage(formattedValue); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [invalidDateText]); + const handleOutsideClick = (target: EventTarget | null) => { if (!iconRef.current?.contains(target as Node)) { setIsOpen(false); @@ -46,20 +51,30 @@ export function Datepicker({ const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; const parsedDate = new Date(fnsParse(newValue, format, new Date())); - const isValidString = !newValue || (newValue.length === format.length && fnsIsValid(parsedDate)); setFormattedValue(newValue); + inputProps.onChange && inputProps.onChange(e); + if (!setErrorMessage(newValue)) { + const result = newValue ? formatISO(parsedDate) : ''; + setDateInvalidError(''); + setCurrentValue(result); + onChange && onChange(result); + } + }; + + const setErrorMessage = (value: string) => { + const newValue = value; + const parsedDate = new Date(fnsParse(newValue, format, new Date())); + const isValidString = !newValue || (newValue.length === format.length && fnsIsValid(parsedDate)); if (!!newValue && !isValidString) { setDateInvalidError(invalidDateText ?? ''); + return true; } else if ( isInRange(parsedDate, calendarProps?.unavailableFrom, calendarProps?.unavailableTo, calendarProps?.unavailable) ) { setDateInvalidError(invalidDateText ?? ''); - } else { - const result = newValue ? formatISO(parsedDate) : ''; - setDateInvalidError(''); - setCurrentValue(result); - onChange && onChange(result); + return true; } + return false; }; const handleCalendarDateChange = (value: string) => { diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts index a6b1a5ed..2b06970a 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts @@ -8,7 +8,7 @@ export interface DatepickerProps { mask?: string; inputProps?: Pick< TextFieldProps, - 'id' | 'label' | 'description' | 'disabled' | 'name' | 'required' | 'size' | 'state' + 'id' | 'label' | 'description' | 'disabled' | 'name' | 'required' | 'size' | 'state' | 'onChange' >; value?: string; label?: string; From 5dd6920de35ace1b1645b6f12c40f47cf52acb5d Mon Sep 17 00:00:00 2001 From: Nicolas T Date: Thu, 7 Mar 2024 13:09:05 +0100 Subject: [PATCH 06/64] V 7.1.1 changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b808b3ce..b2410417 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -Version 7.1.0 +Version 7.1.1 # Changelog @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 7.1.1 - 2024-03-07 ### Added From 1b60163f62294681a7eb4c38596b6fa9d254f5b6 Mon Sep 17 00:00:00 2001 From: Nicolas T Date: Fri, 15 Mar 2024 14:16:53 +0100 Subject: [PATCH 07/64] Datepicker improvements after feedback from afspraken app --- CHANGELOG.md | 16 +++ .../src/molecules/DatepickerExamples.tsx | 101 ++++++++++-------- .../src/molecules/FlyoutExamples.tsx | 3 - .../molecules/datepicker/Calendar.spec.tsx | 7 ++ .../src/lib/molecules/datepicker/Calendar.tsx | 24 +++-- .../molecules/datepicker/Datepicker.spec.tsx | 18 +++- .../datepicker/Datepicker.stories.jsx | 4 +- .../lib/molecules/datepicker/Datepicker.tsx | 35 +++--- .../molecules/datepicker/Datepicker.types.ts | 5 +- .../molecules/datepicker/views/DaysView.tsx | 9 +- .../react-components/src/utils/time.utils.ts | 18 +++- .../react-components/src/utils/utils.spec.tsx | 10 ++ 12 files changed, 174 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2410417..5a86c284 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Datepicker doesn't crash when providing it an invalid date as value +- Controlled Calendar doesn't crash when providing it an invalid date as value + +### Changed + +- Datepicker `onChange` function is now triggered for invalid dates (return empty string) +- Datepicker `onChange` function returns the input string value as second parameter + +### Added + +- Datepicker `errorMsgFunction` to fully customize the error based on the input + ## 7.1.1 - 2024-03-07 ### Added diff --git a/documentation/playground/src/molecules/DatepickerExamples.tsx b/documentation/playground/src/molecules/DatepickerExamples.tsx index 8ad6139f..a4bd8127 100644 --- a/documentation/playground/src/molecules/DatepickerExamples.tsx +++ b/documentation/playground/src/molecules/DatepickerExamples.tsx @@ -23,49 +23,64 @@ export function DatepickerExamples() { />

Datepicker

-
- -
- - - { - return setCustomError(`The wrong date has "${e.target.value.length}" characters`); - } - }} - calendarProps={{ unavailableFrom: new Date('2024-02-24').toISOString() }} - format="dd-MM-yyyy" - value={new Date(Date.now()).toISOString()} - /> -
+
+ +
+ +
+ +
+ { + return setCustomError(`The wrong date has "${e.target.value.length}" characters`); + } + }} + calendarProps={{ unavailableFrom: new Date('2024-02-24').toISOString() }} + format="dd-MM-yyyy" + value={new Date(Date.now()).toISOString()} + /> +
+ { + if (!value || (value && value.includes('00/00'))) return ''; + return 'IT DOES NOT CONTAIN 00/00'; + }} + value={'THIS IS NOT A DATE!'} + inputProps={{ id: 'aui-datepicker-1', state: 'success' }} + calendarProps={{ + unavailable: [new Date(Date.now()).toISOString()], + unavailableTo: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(), + unavailableFrom: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString() + }} + /> +
); } diff --git a/documentation/playground/src/molecules/FlyoutExamples.tsx b/documentation/playground/src/molecules/FlyoutExamples.tsx index 0fca2f2f..253dfb0c 100644 --- a/documentation/playground/src/molecules/FlyoutExamples.tsx +++ b/documentation/playground/src/molecules/FlyoutExamples.tsx @@ -12,9 +12,6 @@ export function FlyoutExamples() { World! Hello}>World! - I am open and uncontrolled}> - By default... - { expect(queryByText('December 2023')).toBeFalsy(); expect(queryByText('January 2024')).toBeTruthy(); }); + + it('should render with wrong input', () => { + const date = 'wrong input'; + render(); + const { baseElement } = render(); + expect(baseElement.innerHTML).toContain(new Date().getFullYear().toString()); + }); }); diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Calendar.tsx b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Calendar.tsx index 3868723c..10fb9cea 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Calendar.tsx +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Calendar.tsx @@ -3,12 +3,13 @@ import { Button } from '../../atoms/button'; import { classNames } from '../../../utils/dom.utils'; import { DaysView } from './views/DaysView'; import { forwardRef, useEffect, useMemo, useState } from 'react'; -import { getMonth, getYear, addMonths, subMonths, format, addYears, subYears, formatISO } from 'date-fns'; +import { getMonth, getYear, addMonths, subMonths, addYears, subYears, formatISO } from 'date-fns'; import { DEFAULT_LOCALE } from '../../../constants/settings'; import { Icon } from '../../base/icon'; import { MonthsView } from './views/MonthsView'; import { YearsView } from './views/YearsView'; import { titleize } from '../../../utils/string.utils'; +import { formatWithFallback } from '../../../utils/time.utils'; export const Calendar = forwardRef( ( @@ -41,21 +42,28 @@ export const Calendar = forwardRef( const [yearsRowsStart, setYearsRowsStart] = useState(activeYear - 7); useEffect(() => { - setActiveDate(value ? new Date(value) : undefined); + const parsedDate = value ? new Date(value) : undefined; + if (isNaN(parsedDate?.getTime() as number)) { + setActiveMonth(getMonth(new Date())); + setActiveYear(getYear(new Date())); + setYearsRowsStart(getYear(new Date()) - 7); + return; + } + return setActiveDate(parsedDate); }, [value]); const activeTimeframeLabels = useMemo(() => { const dateToShow = new Date(activeYear, activeMonth); return { [CalendarView.DAYS]: { - main: titleize(format(dateToShow, 'MMMM yyyy', { locale })), - next: `${ariaLabelNextMonth}, ${format(addMonths(dateToShow, 1), 'MMMM yyyy', { locale })}`, - prev: `${ariaLabelPreviousMonth}, ${format(subMonths(dateToShow, 1), 'MMMM yyyy', { locale })}` + main: titleize(formatWithFallback(dateToShow, 'MMMM yyyy', locale)), + next: `${ariaLabelNextMonth}, ${formatWithFallback(addMonths(dateToShow, 1), 'MMMM yyyy', locale)}`, + prev: `${ariaLabelPreviousMonth}, ${formatWithFallback(subMonths(dateToShow, 1), 'MMMM yyyy', locale)}` }, [CalendarView.MONTHS]: { - main: format(dateToShow, 'yyyy', { locale }), - next: `${ariaLabelNextYear}, ${format(addYears(dateToShow, 1), 'yyyy', { locale })}`, - prev: `${ariaLabelPreviousYear}, ${format(subYears(dateToShow, 1), 'yyyy', { locale })}` + main: formatWithFallback(dateToShow, 'yyyy', locale), + next: `${ariaLabelNextYear}, ${formatWithFallback(addYears(dateToShow, 1), 'yyyy', locale)}`, + prev: `${ariaLabelPreviousYear}, ${formatWithFallback(subYears(dateToShow, 1), 'yyyy', locale)}` }, [CalendarView.YEARS]: { main: `${yearsRowsStart} - ${yearsRowsStart + 17}`, diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx index b5273c08..9e364b1c 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.spec.tsx @@ -89,7 +89,15 @@ describe('UI Components - Molecules - Datepicker', () => { expect(getByText('Ongeldige datum')).toBeInTheDocument(); }); - it('Does not display error message if invalidDateText is wrong', () => { + it('should be able to override the error text with a custom function', () => { + const { baseElement, getByText } = render( 'THIS IS AN ERROR'} />); + const input = baseElement.querySelector('#aui-text-field') as HTMLInputElement; + fireEvent.change(input, { target: { value: 'invalid' } }); + expect(getByText('THIS IS AN ERROR')).toBeInTheDocument(); + render( null} />); + }); + + it('does not display error message if invalidDateText is wrong', () => { /* eslint-disable-next-line */ /* @ts-ignore */ const { baseElement, queryByText } = render(); @@ -99,6 +107,14 @@ describe('UI Components - Molecules - Datepicker', () => { expect(errorMessage).toBeFalsy(); }); + it('should be able to force an error text with a controlled Datepicker', () => { + const { getByText, baseElement } = render(); + expect(getByText('I AM ALWAYS VISIBLE')).toBeInTheDocument(); + const input = baseElement.querySelector('#aui-text-field') as HTMLInputElement; + fireEvent.change(input, { target: { value: '' } }); + expect(getByText('I AM ALWAYS VISIBLE')).toBeInTheDocument(); + }); + it('sets the dateInvalidError state when a date outside the allowed range is entered', () => { const { baseElement, getByText } = render( (null); - const [formattedValue, setFormattedValue] = useState(value ? fnsFormat(new Date(value), format) : ''); + const [formattedValue, setFormattedValue] = useState(value ? formatIfValid(value, format) : ''); const [currentValue, setCurrentValue] = useState(value || ''); const [dateInvalidError, setDateInvalidError] = useState(''); const [isOpen, setIsOpen] = useState(false); @@ -53,25 +54,35 @@ export function Datepicker({ const parsedDate = new Date(fnsParse(newValue, format, new Date())); setFormattedValue(newValue); inputProps.onChange && inputProps.onChange(e); - if (!setErrorMessage(newValue)) { - const result = newValue ? formatISO(parsedDate) : ''; + if (!setErrorMessage(newValue) && fnsIsValid(parsedDate)) { + const result = formatISO(parsedDate); setDateInvalidError(''); setCurrentValue(result); - onChange && onChange(result); + onChange && onChange(result, currentValue); + } else { + onChange && onChange('', currentValue); } }; - const setErrorMessage = (value: string) => { - const newValue = value; + const setErrorMessage = (value: string | undefined) => { + if (errorMsgFunction) { + const message = errorMsgFunction(value); + setDateInvalidError(message ? message : ''); + return !!message; + } + const newValue = value || ''; const parsedDate = new Date(fnsParse(newValue, format, new Date())); const isValidString = !newValue || (newValue.length === format.length && fnsIsValid(parsedDate)); if (!!newValue && !isValidString) { - setDateInvalidError(invalidDateText ?? ''); + setDateInvalidError(invalidDateText === null ? '' : invalidDateText ?? 'Ongeldige datum'); return true; } else if ( isInRange(parsedDate, calendarProps?.unavailableFrom, calendarProps?.unavailableTo, calendarProps?.unavailable) ) { - setDateInvalidError(invalidDateText ?? ''); + setDateInvalidError(invalidDateText === null ? '' : invalidDateText ?? 'Ongeldige datum'); + return true; + } else if (invalidDateText) { + setDateInvalidError(invalidDateText); return true; } return false; @@ -104,7 +115,7 @@ export function Datepicker({ label={undefined} description={undefined} type="text" - value={formattedValue} + value={inputProps.value || formattedValue} onChange={handleChange} state={dateInvalidError ? 'error' : inputProps?.state} /> diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts index 2b06970a..43959a2c 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/Datepicker.types.ts @@ -8,7 +8,7 @@ export interface DatepickerProps { mask?: string; inputProps?: Pick< TextFieldProps, - 'id' | 'label' | 'description' | 'disabled' | 'name' | 'required' | 'size' | 'state' | 'onChange' + 'id' | 'label' | 'description' | 'disabled' | 'name' | 'required' | 'size' | 'state' | 'onChange' | 'value' >; value?: string; label?: string; @@ -16,7 +16,8 @@ export interface DatepickerProps { invalidDateText?: string; iconButtonLabel?: string; calendarProps?: DatepickerCalendarProps; - onChange?: (value: string) => void; + errorMsgFunction?: (value: string | undefined) => string | undefined | null; + onChange?: (value: string, inputValue?: string) => void; } export interface CalendarProps { diff --git a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/views/DaysView.tsx b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/views/DaysView.tsx index b8ed26d0..b203f567 100644 --- a/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/views/DaysView.tsx +++ b/packages/antwerp-ui/react-components/src/lib/molecules/datepicker/views/DaysView.tsx @@ -7,7 +7,8 @@ import { format, isAfter, startOfMonth, - startOfWeek + startOfWeek, + isValid } from 'date-fns'; import { DaysViewProps } from '../Datepicker.types'; import { DayButton } from './DayButton'; @@ -28,11 +29,11 @@ export function DaysView({ const days: React.ReactElement[] = []; let index = startOfWeek(weekDay, { locale }); const endWeek = endOfWeek(weekDay, { locale }); - while (!isAfter(index, endWeek)) { + while (isValid(index) && !isAfter(index, endWeek)) { const activeMonthYear = new Date(activeYear, activeMonth); days.push( isSameDay(date, new Date(u))) ); } + +export function formatIfValid(date: string, dateFormat: string) { + try { + return format(new Date(date), dateFormat); + } catch (_e) { + return date; + } +} + +export function formatWithFallback(date: Date, dateFormat: string, locale?: Locale, fallback = '') { + try { + return format(date, dateFormat, { locale: locale }); + } catch (_e) { + return fallback; + } +} diff --git a/packages/antwerp-ui/react-components/src/utils/utils.spec.tsx b/packages/antwerp-ui/react-components/src/utils/utils.spec.tsx index 3d2925c5..decaac87 100644 --- a/packages/antwerp-ui/react-components/src/utils/utils.spec.tsx +++ b/packages/antwerp-ui/react-components/src/utils/utils.spec.tsx @@ -3,6 +3,7 @@ import { renderHTMLLink } from './render.utils'; import { getPosition, getSteps, getValueFromPosition, pagesArray } from './math.utils'; import { invalidIcon } from './file.utils'; import jest from 'jest-mock'; +import { formatIfValid } from './time.utils'; describe('Utils - DOM Utils', () => { describe('- classNames', () => { @@ -173,3 +174,12 @@ describe('Utils - File Utils', () => { }); }); }); + +describe('Utils - Time Utils', () => { + describe('- formatIfValid', () => { + it('formats a date string or return raw string', () => { + expect(formatIfValid('2023-02-22', 'dd/MM/yyyy')).toEqual('22/02/2023'); + expect(formatIfValid('MEH', 'yyyy/MM/dd')).toEqual('MEH'); + }); + }); +}); From 29724775525bc49b8e0d2604f6d3aafdfa2bdfd0 Mon Sep 17 00:00:00 2001 From: Jasper Van Proeyen Date: Wed, 20 Mar 2024 10:26:52 +0100 Subject: [PATCH 08/64] =?UTF-8?q?=F0=9F=92=84=20Core=20branding=20update?= =?UTF-8?q?=206.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ documentation/playground/index.html | 4 ++-- documentation/storybook/.storybook/branding-versions.json | 2 +- documentation/storybook/.storybook/preview-head.html | 2 +- packages/antwerp-ui/react-components/README.md | 4 ++-- .../antwerp-ui/react-components/src/constants/settings.ts | 2 +- .../src/lib/organisms/header/Header.stories.jsx | 4 ++-- .../react-components/src/lib/organisms/header/Header.tsx | 2 +- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2410417..fead04e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Core branding update 6.6.0 + ## 7.1.1 - 2024-03-07 ### Added diff --git a/documentation/playground/index.html b/documentation/playground/index.html index d75c2f98..11d46cbb 100644 --- a/documentation/playground/index.html +++ b/documentation/playground/index.html @@ -5,8 +5,8 @@ Codestin Search App - - + + diff --git a/documentation/storybook/.storybook/branding-versions.json b/documentation/storybook/.storybook/branding-versions.json index c2152238..1176e865 100644 --- a/documentation/storybook/.storybook/branding-versions.json +++ b/documentation/storybook/.storybook/branding-versions.json @@ -1,3 +1,3 @@ { - "core_branding_scss": "6.5.0" + "core_branding_scss": "6.6.0" } diff --git a/documentation/storybook/.storybook/preview-head.html b/documentation/storybook/.storybook/preview-head.html index 044705b8..841ffce2 100644 --- a/documentation/storybook/.storybook/preview-head.html +++ b/documentation/storybook/.storybook/preview-head.html @@ -24,7 +24,7 @@ white-space: normal !important; } - +