diff --git a/src/components/calendar/README.md b/src/components/calendar/README.md index 3efb92ff386..31c15a3d176 100644 --- a/src/components/calendar/README.md +++ b/src/components/calendar/README.md @@ -282,6 +282,23 @@ Notes: - `year`, `month` and `day` will always be shown. If you need to leave out a value, set the property to `undefined`, although this is highly discouraged for accessibility reasons +### Weekday name header format + +2.12.0+ + +The calendar weekday name header format defaults to `'short'`, which is typically a three-character +abbreviation of the weekday, although some [locales](#internationalization) may override this. The +format can be controlled via the prop `weekday-header-format` and accepts one of three values: + +- `'long'` the full weekday name (e.g. Tuesday). Handy when using a full width + calendar. Avoid using with the default calendar width. +- `'short'` typically is a 2 or 3 letter abbreviation of the weekday name, depending on the selected + locale (e.g. "Tue"). +- `'narrow'` is typically a single character abbreviation (e.g., T). Two weekdays may + have the same narrow style for some locales (e.g. Tuesday and Thursday's narrow style are both + T). This can be handy for those locales that do not support the `'short'` format, + such as locales `'ar'` and `'fa'`. + ### Hiding the top selected date header By default, the current selected date will be displayed at the top of the calendar component, @@ -592,6 +609,7 @@ the same locale as requested, depending on the supported locales of `Intl`). labelHelp: 'Mit den Pfeiltasten durch den Kalender navigieren' }, 'ar-EG': { + weekdayHeaderFormat: 'narrow', labelPrevDecade: 'العقد السابق', labelPrevYear: 'العام السابق', labelPrevMonth: 'الشهر السابق', @@ -607,6 +625,7 @@ the same locale as requested, depending on the supported locales of `Intl`). labelHelp: 'استخدم مفاتيح المؤشر للتنقل في التواريخ' }, zh: { + weekdayHeaderFormat: 'narrow', labelPrevDecade: '过去十年', labelPrevYear: '上一年', labelPrevMonth: '上个月', diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index b1a9e5e42ef..5392a5a8d24 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -42,6 +42,14 @@ const NAME = 'BCalendar' // Key Codes const { UP, DOWN, LEFT, RIGHT, PAGEUP, PAGEDOWN, HOME, END, ENTER, SPACE } = KeyCodes +// Common calendar option value strings +export const STR_GREGORY = 'gregory' +export const STR_NUMERIC = 'numeric' +export const STR_2_DIGIT = '2-digit' +export const STR_LONG = 'long' +export const STR_SHORT = 'short' +export const STR_NARROW = 'narrow' + // --- BCalendar component --- // @vue/component @@ -224,13 +232,25 @@ export const BCalendar = Vue.extend({ }, dateFormatOptions: { // `Intl.DateTimeFormat` object + // Note: This value is *not* to be placed in the global config type: Object, default: () => ({ - year: 'numeric', - month: 'long', - day: 'numeric', - weekday: 'long' + year: STR_NUMERIC, + month: STR_LONG, + day: STR_NUMERIC, + weekday: STR_LONG }) + }, + weekdayHeaderFormat: { + // Format of the weekday names at the top of the calendar + // Note: This value is *not* to be placed in the global config + type: String, + // `short` is typically a 3 letter abbreviation, + // `narrow` is typically a single letter + // `long` is the full week day name + // Although some locales may override this (i.e `ar`, etc) + default: STR_SHORT, + validator: value => arrayIncludes([STR_LONG, STR_SHORT, STR_NARROW], value) } }, data() { @@ -271,18 +291,18 @@ export const BCalendar = Vue.extend({ }, computedLocale() { // Returns the resolved locale used by the calendar - return resolveLocale(concat(this.locale).filter(identity), 'gregory') + return resolveLocale(concat(this.locale).filter(identity), STR_GREGORY) }, calendarLocale() { // This locale enforces the gregorian calendar (for use in formatter functions) // Needed because IE 11 resolves `ar-IR` as islamic-civil calendar // and IE 11 (and some other browsers) do not support the `calendar` option // And we currently only support the gregorian calendar - const fmt = new Intl.DateTimeFormat(this.computedLocale, { calendar: 'gregory' }) + const fmt = new Intl.DateTimeFormat(this.computedLocale, { calendar: STR_GREGORY }) const calendar = fmt.resolvedOptions().calendar let locale = fmt.resolvedOptions().locale /* istanbul ignore if: mainly for IE 11 and a few other browsers, hard to test in JSDOM */ - if (calendar !== 'gregory') { + if (calendar !== STR_GREGORY) { // Ensure the locale requests the gregorian calendar // Mainly for IE 11, and currently we can't handle non-gregorian calendars // TODO: Should we always return this value? @@ -384,9 +404,9 @@ export const BCalendar = Vue.extend({ // Ensure we have year, month, day shown for screen readers/ARIA // If users really want to leave one of these out, they can // pass `undefined` for the property value - year: 'numeric', - month: '2-digit', - day: '2-digit', + year: STR_NUMERIC, + month: STR_2_DIGIT, + day: STR_2_DIGIT, // Merge in user supplied options ...this.dateFormatOptions, // Ensure hours/minutes/seconds are not shown @@ -395,26 +415,37 @@ export const BCalendar = Vue.extend({ minute: undefined, second: undefined, // Ensure calendar is gregorian - calendar: 'gregory' + calendar: STR_GREGORY }) }, formatYearMonth() { // Returns a date formatter function return createDateFormatter(this.calendarLocale, { - year: 'numeric', - month: 'long', - calendar: 'gregory' + year: STR_NUMERIC, + month: STR_LONG, + calendar: STR_GREGORY }) }, formatWeekdayName() { - return createDateFormatter(this.calendarLocale, { weekday: 'long', calendar: 'gregory' }) + // Long weekday name for weekday header aria-label + return createDateFormatter(this.calendarLocale, { + weekday: STR_LONG, + calendar: STR_GREGORY + }) }, formatWeekdayNameShort() { - // Used as the header cells - return createDateFormatter(this.calendarLocale, { weekday: 'short', calendar: 'gregory' }) + // Weekday header cell format + // defaults to 'short' 3 letter days, where possible + return createDateFormatter(this.calendarLocale, { + weekday: this.weekdayHeaderFormat || STR_SHORT, + calendar: STR_GREGORY + }) }, formatDay() { - return createDateFormatter(this.calendarLocale, { day: 'numeric', calendar: 'gregory' }) + return createDateFormatter(this.calendarLocale, { + day: STR_NUMERIC, + calendar: STR_GREGORY + }) }, // Disabled states for the nav buttons prevDecadeDisabled() { diff --git a/src/components/calendar/package.json b/src/components/calendar/package.json index f64b8f18a43..89771389ac2 100644 --- a/src/components/calendar/package.json +++ b/src/components/calendar/package.json @@ -172,6 +172,11 @@ "prop": "dateFormatOptions", "version": "2.6.0", "description": "Format object for displayed text string that is passed to `Intl.DateTimeFormat`" + }, + { + "prop": "weekdayHeaderFormat", + "version": "2.12.0", + "description": "Format to use for the calendar weekday headings. Possible values are `long`, `short` (default), or `narrow`" } ], "events": [ diff --git a/src/components/form-datepicker/README.md b/src/components/form-datepicker/README.md index 61e1cd5a269..5e161f97dca 100644 --- a/src/components/form-datepicker/README.md +++ b/src/components/form-datepicker/README.md @@ -439,6 +439,23 @@ Notes: - `year`, `month` and `day` will always be shown. If you need to leave out a value, set the property to `undefined`, although this is highly discouraged for accessibility reasons +### Weekday name header format + +2.12.0+ + +The calendar weekday name header format defaults to `'short'`, which is typically a three-character +abbreviation of the weekday, although some [locales](#internationalization) may override this. The +format can be controlled via the prop `weekday-header-format` and accepts one of three values: + +- `'long'` the full weekday name (e.g. Tuesday). Handy when using a full width + calendar. Avoid using with the default calendar width. +- `'short'` typically is a 2 or 3 letter abbreviation of the weekday name, depending on the selected + locale (e.g. "Tue"). +- `'narrow'` is typically a single character abbreviation (e.g., T). Two weekdays may + have the same narrow style for some locales (e.g. Tuesday and Thursday's narrow style are both + T). This can be handy for those locales that do not support the `'short'` format, + such as locales `'ar'` and `'fa'`. + ### Date navigation button slots 2.12.0+ @@ -551,6 +568,7 @@ Saturday. labelHelp: 'Mit den Pfeiltasten durch den Kalender navigieren' }, 'ar-EG': { + weekdayHeaderFormat: 'narrow', labelPrevDecade: 'العقد السابق', labelPrevYear: 'العام السابق', labelPrevMonth: 'الشهر السابق', @@ -566,6 +584,7 @@ Saturday. labelHelp: 'استخدم مفاتيح المؤشر للتنقل في التواريخ' }, zh: { + weekdayHeaderFormat: 'narrow', labelPrevDecade: '过去十年', labelPrevYear: '上一年', labelPrevMonth: '上个月', diff --git a/src/components/form-datepicker/form-datepicker.js b/src/components/form-datepicker/form-datepicker.js index 3ede9083344..af73200e181 100644 --- a/src/components/form-datepicker/form-datepicker.js +++ b/src/components/form-datepicker/form-datepicker.js @@ -1,4 +1,5 @@ import Vue from '../../utils/vue' +import { arrayIncludes } from '../../utils/array' import { BVFormBtnLabelControl, dropdownProps } from '../../utils/bv-form-btn-label-control' import { getComponentConfig } from '../../utils/config' import { createDate, constrainDate, formatYMD, parseYMD } from '../../utils/date' @@ -6,7 +7,7 @@ import { isUndefinedOrNull } from '../../utils/inspect' import { pick } from '../../utils/object' import idMixin from '../../mixins/id' import { BButton } from '../button/button' -import { BCalendar } from '../calendar/calendar' +import { BCalendar, STR_LONG, STR_NARROW, STR_NUMERIC, STR_SHORT } from '../calendar/calendar' import { BIconCalendar, BIconCalendarFill } from '../../icons/icons' const NAME = 'BFormDatepicker' @@ -240,14 +241,26 @@ const propsMixin = { }, dateFormatOptions: { // `Intl.DateTimeFormat` object + // Note: This value is *not* to be placed in the global config type: Object, default: () => ({ - year: 'numeric', - month: 'long', - day: 'numeric', - weekday: 'long' + year: STR_NUMERIC, + month: STR_LONG, + day: STR_NUMERIC, + weekday: STR_LONG }) }, + weekdayHeaderFormat: { + // Format of the weekday names at the top of the calendar + // Note: This value is *not* to be placed in the global config + type: String, + // `short` is typically a 3 letter abbreviation, + // `narrow` is typically a single letter + // `long` is the full week day name + // Although some locales may override this (i.e `ar`, etc) + default: STR_SHORT, + validator: value => arrayIncludes([STR_LONG, STR_SHORT, STR_NARROW], value) + }, // Dark mode dark: { type: Boolean, @@ -328,7 +341,8 @@ export const BFormDatepicker = /*#__PURE__*/ Vue.extend({ labelCalendar: self.labelCalendar, labelNav: self.labelNav, labelHelp: self.labelHelp, - dateFormatOptions: self.dateFormatOptions + dateFormatOptions: self.dateFormatOptions, + weekdayHeaderFormat: self.weekdayHeaderFormat } }, computedLang() { diff --git a/src/components/form-datepicker/package.json b/src/components/form-datepicker/package.json index 7c05c0e0751..2f07d5f3982 100644 --- a/src/components/form-datepicker/package.json +++ b/src/components/form-datepicker/package.json @@ -268,6 +268,11 @@ "prop": "dateFormatOptions", "version": "2.6.0", "description": "Format object for displayed text string that is passed to `Intl.DateTimeFormat`" + }, + { + "prop": "weekdayHeaderFormat", + "version": "2.12.0", + "description": "Format to use for the calendar weekday headings. Possible values are `long`, `short` (default), or `narrow`" } ], "events": [