telekom / scale

Scale is the digital design system for Telekom products and experiences.
https://telekom.github.io/scale/
Other
374 stars 82 forks source link

Datepicker formatter function is not working in React 18 #1479

Closed tschinski closed 1 year ago

tschinski commented 1 year ago

Scale Version 3.0.0-beta.116

Framework and version React 18.2.0

Current Behavior Formatter function in dateAdapter Object is not working any more as in React V 17 and lower. You will notice a short flickering with the formatted value, but then the value get set one more time unformatted.

Expected Behavior Formatter function formats the value and set it in date picker.

marvinLaubenstein commented 1 year ago

Hi @tschinski, thanks for your ticket.

I just tried a variant in React 18.2.0 and I get it displayed correctly.

Could you possibly provide us with a code snippet of how you are currently using the component ?

tschinski commented 1 year ago

Hi @marvinLaubenstein,

yes sure!

In some React Component ...

const DATE_FORMAT = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/;
const [datePickerValue, setDatePickerValue] = useState<Date>(new Date());

const getDatePickerValue = () => {
  const month = datePickerValue.getMonth() + 1;
  const formattedMonth = month < 10 ? `0${month}` : month;
  return `${datePickerValue.getFullYear()}-${formattedMonth}-${datePickerValue.getDate()}`;
};

return (
  <ScaleDatePicker
    label="Test"
    value={getDatePickerValue()}
    onScaleChange={e => setDatePickerValue(new Date(e.detail.value))}
    dateAdapter={{
      parse(value = "", createDate: any) {
        const matches = value.match(DATE_FORMAT);
        if (matches) {
          // ...
        }
      },
      format(date: any) {
        const month = date.getMonth() + 1;
        const formattedMonth = month < 10 ? `0${month}` : month;
        return `${date.getDate()}.${formattedMonth}.${date.getFullYear()}`;
      }
    }}
  />
)
marvinLaubenstein commented 1 year ago

Thanks for the snippet. I hope my solution helps you. If not, then please contact me again.

https://codesandbox.io/s/scale-react-date-picker-ik2f6o?file=/Test.js:2374-2390

And just the code (unfortunately it has become a bit more code):

import React, { useEffect, useRef, useState } from "react";
import reactifyWc from "reactify-wc";
const ScaleDatePicker = reactifyWc("scale-date-picker");

const Test = () => {
  const pickerRef = useRef(null);
  const [inputValue, setInputValue] = useState("");
  const [pushedInputValue, setPushedInputValue] = useState("");

  const handleClick = () => {
    setPushedInputValue(convertToEnglishDate(inputValue));
    setInputValue("");
  };

  const convertToEnglishDate = (date) => {
    const dateParts = date.split(".");
    const year = dateParts[2];
    const month = dateParts[1];
    const day = dateParts[0];
    return `${year}-${month}-${day}`;
  };

  useEffect(() => {
    const picker = pickerRef.current;
    picker.localization = {
      buttonLabel: "Datum wählen",
      placeholder: "TT.MM.JJJJ",
      selectedDateMessage: "Gewähltes Datum",
      prevMonthLabel: "Vorheriger Monat",
      nextMonthLabel: "Nächster Monat",
      monthSelectLabel: "Monat",
      yearSelectLabel: "Jahr",
      closeLabel: "Fenster schließen",
      keyboardInstruction:
        "Sie können mit den Pfeiltasten vor und zurück navigieren",
      calendarHeading: "Datum wählen",
      dayNames: [
        "Sonntag",
        "Montag",
        "Dienstag",
        "Mittwoch",
        "Donnerstag",
        "Freitag",
        "Samstag"
      ],
      monthNames: [
        "Januar",
        "Februar",
        "März",
        "April",
        "Mai",
        "Juni",
        "Juli",
        "August",
        "September",
        "Oktober",
        "November",
        "Dezember"
      ],
      monthNamesShort: [
        "Jan",
        "Feb",
        "Mär",
        "Apr",
        "Mai",
        "Jun",
        "Jul",
        "Aug",
        "Sep",
        "Okt",
        "Nov",
        "Dez"
      ],
      today: "heute",
      locale: "de-DE"
    };

    const DATE_FORMAT = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/;
    picker.dateAdapter = {
      parse(value = "", createDate) {
        const matches = value.match(DATE_FORMAT);
        if (matches) {
          return createDate(matches[3], matches[2], matches[1]);
        }
      },
      format(date) {
        return `${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}`;
      }
    };
  }, []);

  return (
    <>
      <ScaleDatePicker
        label="Test Calendar"
        ref={pickerRef}
        value={pushedInputValue}
      ></ScaleDatePicker>
      <p>
        Warning: Please push only if a complete german date is entered (This is
        only a test case without checks)!
      </p>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <button onClick={handleClick}>Push input into calendar </button>
    </>
  );
};

export default Test;
tschinski commented 1 year ago

Hi Marvin,

thanks for your snippet.

But this doesn't help us.

With this example we can't track the value of the DatePicker, when the /

tschinski commented 1 year ago

i meant the "input / button combination"

marvinLaubenstein commented 1 year ago

Oh ok, then I misunderstood your issue.

I see the problem. As soon as a new date value is passed via getDatePickerValue into the value property, the date syntax changes back to English and overwrites the German one.

We will look into it. Thank you for explaining again.

tschinski commented 1 year ago

Exactly! Thanks for your efforts!

marvinLaubenstein commented 1 year ago

Sorry, just one more question about your code:

Is there a specific reason that the output value should be passed back to the value property of the scale-date-picker component ?

tschinski commented 1 year ago

Yes, this is standard in React. It's called a controlled component. https://reactjs.org/docs/forms.html#controlled-components

tschinski commented 1 year ago

We use all your components like this

const [inputState, setInputState] = useState('');

<ScaleTextInput value={inputState} onScaleChange={(e) => setInputState(e.target.value) ...>

felix-ico commented 1 year ago

hi @tschinski I tried modifying your code a bit to make the date picker controlled by the react component, could you have a look and see if it works as you expect it to? https://codesandbox.io/s/scale-components-react18-with-wrapper-forked-j28er4?file=/src/App.js

tschinski commented 1 year ago

Ok cool, the key does the trick ;) ! Thanks for your help, you can close the issue!