Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DatePicker] Customize day headers #4605

Closed
gReis89 opened this issue May 22, 2020 · 23 comments · Fixed by #5373
Closed

[DatePicker] Customize day headers #4605

gReis89 opened this issue May 22, 2020 · 23 comments · Fixed by #5373
Assignees
Labels
bug 🐛 Something doesn't work component: DatePicker The React component. component: pickers This is the name of the generic UI component, not the React module!

Comments

@gReis89
Copy link

gReis89 commented May 22, 2020

I'm trying to customize the DatePicker designs as close as possible to my requirements. We are using 3 letters(mon - tue - wed...) to describe our week days but I can't see any option where I can customize the default headers(M - T - W...).

Screen Shot 2020-05-22 at 19 09 45

@gReis89 gReis89 changed the title How Override How to override date picker headers? May 22, 2020
@dmtrKovalenko
Copy link
Member

dmtrKovalenko commented Jul 15, 2020

It is possible using date-io adapter inheritance. Here is a guide https://next.material-ui-pickers.dev/guides/date-io-customization#override-date-logic

You need to extend getWeekdays method and simply return an array with 7 strings. But make sure that your weekdays will be properly displayed for different locales 😉

@oliviertassinari
Copy link
Member

Do we have a demo for it? What about we add one?

I have included the concern in mui/material-ui#20960.

@tpeek
Copy link

tpeek commented Nov 22, 2020

@dmtrKovalenko Unfortunately, it looks like the behavior for showing just one character for the weekday is hardcoded:

https://github.com/mui-org/material-ui/blob/9b1f6f72b51a72910a5dff1b47b9d351629bda51/packages/material-ui-lab/src/DayPicker/PickersCalendar.tsx#L180
It doesn't matter what getWeekdays returns, the date picker will always show just the first letter.

@oliviertassinari oliviertassinari changed the title How to override date picker headers? [DatePicker] Customize day headers Nov 22, 2020
@oliviertassinari oliviertassinari transferred this issue from mui/material-ui-pickers Nov 22, 2020
@oliviertassinari oliviertassinari added bug 🐛 Something doesn't work component: DatePicker The React component. labels Nov 22, 2020
@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 22, 2020

@tpeek Thanks for reporting the issue. What I have found during my investigation:

two-letter weekday abbreviations. The purpose of these is for things like calendar pickers, thus they should be as small as possible.

  • date-fns is implemented with EEEEEE it's 2 chars.
  • The format were designed to abstract between different date engines, but that much to handle customizability.
  • The browser offers an API to solve this problem but isn't close enough
var date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
console.log(new Intl.DateTimeFormat('fr-FR', { weekday: 'short' }).format(date)); // 'dim.'

@marthaerm
Copy link

Hi! @oliviertassinari is this issue good to take? I would like to give it a try :) This would be my first contribution.

@oliviertassinari
Copy link
Member

@marthaerm Mind that this issue isn't straightforward. We recommend issues with the "good first issue" label to get started https://github.com/mui-org/material-ui/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22

@brorlarsnicklas
Copy link

Hi, is anyone working on this at the moment? If not, what is the intended approach right now? I'd like to work on it if you can guide me in the direction that you would want.

@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 9, 2020

@brorlarsnicklas Nobody working on it. I think that resolving this issue will involve internalizing date-io in here. I also think that it involves reconsidering what's the best customization API. It's a large effort.

@brorlarsnicklas
Copy link

@oliviertassinari oh, I see. Then it might be better for someone else to work with it hehe! :D

@yevheniiminin
Copy link

@oliviertassinari any chance that you could somehow allow to overwrite this behavior? It's the only thing that's blocking us from updating to v5

@HossamHoussien
Copy link

If you're lazy to read through, just jump this code snippet to see a complete working snippet

I encourage you to read the following...

For anyone struggling to get this done as I did. I came to a hacky workaround to overcome this issue.

Since the component is set in stone to always get the first character from the provided weekdays given by the date-io adapter https://github.com/mui-org/material-ui/blob/9b1f6f72b51a72910a5dff1b47b9d351629bda51/packages/material-ui-lab/src/DayPicker/PickersCalendar.tsx#L173-L182

Note

  • No matter what is the return value of getWeekdays() it will always take the first character.

My hack to get it done:
By extending the Array.prototype.chartAt to return the first "element" so all you need to do is to add this line in your codebase*

Array.prototype.charAt = function(index) {
  return this[index];
};

Then by updating the used locale (or providing a custom locale if you need more customization) to override the weekdaysShort labels, for example, I'm using moment here.

moment.updateLocale('en-gb', {

  // Default value from moment
  // weekdaysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),

  // New value
  weekdaysShort: [["Sun"], ["Mon"], ["Tue"], ["Wed"], ["Thu"], ["Fri"], ["Sat"]]
});

A complete working snippet can be found here https://codesandbox.io/s/nervous-pike-rv9sn?file=/src/App.js

Note

@HossamHoussien
Copy link

HossamHoussien commented Nov 24, 2021

Another approach

Another solution inspired by @dmtrKovalenko's comment above

Simply, provide a custom DateAdapter with a little tweak to it to make it work with the current implementation of Mui DatePickers.

// CustomDateAdapter.js
import DateIOAdapter from "@mui/lab/AdapterMoment";

export default function CustomAdapter(options) {
  const adapter = new DateIOAdapter(options);

  const constructDayObject = (day) => ({ charAt: () => day });

  return {
    ...adapter,

    getWeekdays() {
      // Feel free to replace this with your custom value
      // e.g const customWeekdays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
      const customWeekdays = adapter.getWeekdays();

      return customWeekdays.map((day) => constructDayObject(day));
    }
  };
}
// Your cool component

import React from "react";
import moment from "moment";
import StaticDatePicker from "@mui/lab/StaticDatePicker";
import LocalizationProvider from "@mui/lab/LocalizationProvider";

import CustomDateAdapter from "./CustomDateAdapter";

export default function index() {
  return (
    <LocalizationProvider dateAdapter={CustomDateAdapter}>
      {/* This can be replaced by any other DatePicker component */}
      <StaticDatePicker
        openTo="day"
        showToolbar={false}
        displayStaticWrapperAs="desktop"
        value={moment()}
        onChange={() => {}}
        renderInput={(params) => <input {...params} />}
      />
    </LocalizationProvider>
  );
}

I prefer this solution over my previous one as it won't mess up the native objects as Array. You can find a complete working snippet here https://codesandbox.io/s/twilight-sky-t7dto?file=/src/App.js

@zastrich
Copy link

Beautiful hack, i implement another step to showing weekdays without UPPERCASE, much more elegant.

import DateIOAdapter from "@mui/lab/AdapterMoment";

export default function CustomDateAdapter(options) {
  const adapter = new DateIOAdapter(options);

  const constructUpperObject = (text) => ({ toUpperCase: () => text });
  const constructDayObject = (day) => ({ charAt: () => constructUpperObject(day) });

  return {
    ...adapter,

    getWeekdays() {
      // Feel free to replace this with your custom value
      // e.g const customWeekdays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
      const customWeekdays = adapter.getWeekdays();

      return customWeekdays.map((day) => constructDayObject(day));
    }
  };
}

See snippet here

@avs-git
Copy link

avs-git commented Feb 18, 2022

Another solution inspired by @HossamHoussien comment for TypeScript.

import AdapterDateFns from '@mui/lab/AdapterDateFns';

class CustomString extends String {
  charAt(_: number): string {
    return this.valueOf();
  }
}

const weekDays = ['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс'];
const customWeekDays = weekDays.map((day) => new CustomString(day) as string);

export class DateAdapter extends AdapterDateFns {
  getWeekdays = (): string[] => customWeekDays;
}

This solution is safety for using with TypeScript and needs almost no casting. But it needs some changing in CSS to avoid toUppercase effect.

@ghm-salvarado
Copy link

Thanks @avs-git, that works well. You can avoid the toUppercase effect here too instead of messing with CSS, but it requires using any (unless there's something I'm missing):

class CustomString extends String {
  charAt(_: number): any {
    return {
      toUpperCase: () => this.valueOf()
    }
  }
}

@flaviendelangle flaviendelangle transferred this issue from mui/material-ui Apr 21, 2022
@flaviendelangle flaviendelangle added the component: pickers This is the name of the generic UI component, not the React module! label Apr 21, 2022
@maapteh
Copy link

maapteh commented May 3, 2022

So many hacks needed for something so small. So when the adapter/lib tells me a short weekDay is Ma. then the component just thinks M is better to display? I really hope you can fix it in the core

@vshjxyz
Copy link

vshjxyz commented May 18, 2022

Hello, I've been trying to edit the headers as well by extending the adapter as suggested, however, this line prevents any edit as it always forces the picker to use the first character of the day name to uppercase

{utils.getWeekdays().map((day, i) => (
<PickersCalendarWeekDayLabel aria-hidden key={day + i.toString()} variant="caption">
{day.charAt(0).toUpperCase()}
</PickersCalendarWeekDayLabel>
))}

I know this would be a breaking change, but I really think the .charAt(0).toUpperCase() should be removed. Alternatively, if we know that the extender is not the default one (tricky part), then we can avoid that formatting and leave it "As-is"

@ozgurbaserdem
Copy link

ozgurbaserdem commented Jun 23, 2022

Hey ghm-salvarado! Thank you for this work around, it does look smooth but when we run it in our app we get the following TypeError:
Uncaught TypeError: String.prototype.valueOf requires that 'this' be a String at CustomString.valueOf
Has anyone else came across the same issue with this work around?

As a clarification this occurs when we use:

class CustomString extends String {
  charAt(_: number): any {
    return {
      toUpperCase: () => this.valueOf()
    }
  }
}
const weekDaysSwe = ["Mån", "Tis", "Ons", "Tors", "Fre", "Lör", "Sön"];
const customWeekDaysSwe = weekDaysSwe.map(
  (day) => new CustomString(day) as string
);

export class SvLocalizationUtil extends AdapterDateFns {
  getWeekdays = (): string[] => customWeekDaysSwe;
}

I tried to remove the any prop, and the toUpperCase function as well and use avs-git solution as well:

class CustomString extends String {
  charAt(_: number): string {
    return this.valueOf();
  }
}
const weekDaysSwe = ["Mån", "Tis", "Ons", "Tors", "Fre", "Lör", "Sön"];
const customWeekDaysSwe = weekDaysSwe.map(
  (day) => new CustomString(day) as string
);

export class SvLocalizationUtil extends AdapterDateFns {
  getWeekdays = (): string[] => customWeekDaysSwe;
}

But the same TypeError issue persists.

Should also mention that I use:
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
and not
import AdapterDateFns from '@mui/lab/AdapterDateFns';

@nakul952
Copy link

I'm trying to customize the DatePicker designs as close as possible to my requirements. We are using 3 letters(mon - tue - wed...) to describe our week days but I can't see any option where I can customize the default headers(M - T - W...).

Screen Shot 2020-05-22 at 19 09 45

now I'm facing the same issue on my react project but I have found a solution
it would help if you used this prop
dayOfWeekFormatter={(day) => ${day}}
Screenshot 2023-06-26 120049

@fernandoem88
Copy link

if someone else has this issue: my solution is to use a ref that goes from 0 - 7 modulo.

const weekdayRef = useRef(0);

          const dayOfWeekFormatter = (firstDayLetter: string) => {
          const weekday = weekdayRef.current + 1;
          weekdayRef.current = weekday % 7;
          return <Box>{weekday}</Box>
}
// every time the component re-renders, the ref restarts from 0 to 7

@LukasTy
Copy link
Member

LukasTy commented Apr 3, 2024

@fernandoem88 Which version of @mui/x-date-pickers are you using?
On v6.16.0 we have introduced a second argument to the dayOfWeekFormatter, if you are using that version or above, consider refactoring your code to avoid the need for useRef. 😉
P.S. On v7 we have ditched the first string argument and kept only the date object.

@joaccocaballero
Copy link

How can I change color of day headers? using Slot props in datepicker component

@LukasTy
Copy link
Member

LukasTy commented Apr 19, 2024

How can I change color of day headers? using Slot props in datepicker component

Hey @joaccocaballero, have you checked this playground? 🤔
Try selecting the DayCalendar component and the weekDayLabel slot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: DatePicker The React component. component: pickers This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging a pull request may close this issue.