diff --git a/.changeset/wicked-eels-call.md b/.changeset/wicked-eels-call.md new file mode 100644 index 0000000000..ff73fe3bb4 --- /dev/null +++ b/.changeset/wicked-eels-call.md @@ -0,0 +1,5 @@ +--- +'@solid-design-system/components': patch +--- + +Fixed the `sd-datepicker` css properties not showing in Storybook. diff --git a/packages/components/src/components/datepicker/datepicker-converters.ts b/packages/components/src/components/datepicker/datepicker-converters.ts new file mode 100644 index 0000000000..71966bed39 --- /dev/null +++ b/packages/components/src/components/datepicker/datepicker-converters.ts @@ -0,0 +1,114 @@ +export const isoDateConverter = { + fromAttribute(value: string | null): string | null { + if (!value) return null; + + // normalize all separators to hyphens + const cleaned = value.trim().replace(/[./]/g, '-'); + + // acccept YYYY-MM-DD only + const match = cleaned.match(/^(\d{4})-(\d{2})-(\d{2})$/); + if (!match) return null; + + const iso = `${match[1]}-${match[2]}-${match[3]}`; + + // validate date + const d = new Date(+match[1], +match[2] - 1, +match[3]); + if (d.getFullYear() !== +match[1] || d.getMonth() !== +match[2] - 1 || d.getDate() !== +match[3]) { + return null; + } + + return iso; + }, + + toAttribute(value: string | null): string { + return value ?? ''; + } +}; + +export const viewMonthConverter = { + fromAttribute(value: string | null): Date | null { + if (!value) return null; + + const cleaned = value.trim().replace(/[./]/g, '-'); + let m: RegExpMatchArray | null = cleaned.match(/^(\d{2})-(\d{4})$/); + let month: number; + let year: number; + + if (m) { + month = Number(m[1]); + year = Number(m[2]); + } else { + m = cleaned.match(/^(\d{4})-(\d{2})$/); + if (!m) return null; + year = Number(m[1]); + month = Number(m[2]); + } + + if (!year || !month || month < 1 || month > 12) return null; + + return new Date(year, month - 1, 1); + }, + + toAttribute(value: Date | null): string { + if (!value) return ''; + const year = value.getFullYear(); + const month = String(value.getMonth() + 1).padStart(2, '0'); + return `${year}-${month}`; + } +}; + +export const disabledDatesConverter = { + fromAttribute(value: string | null): string[] { + if (!value) return []; + + let rawList: string[] = []; + + const trimmed = value.trim(); + + // arrays + if (trimmed.startsWith('[')) { + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const parsed = JSON.parse(trimmed); + if (Array.isArray(parsed)) { + rawList = parsed.map(String); + } + } catch { + return []; + } + } else { + // space separated + rawList = trimmed + .split(/[\s,]+/) + .map(v => v.trim()) + .filter(Boolean); + } + + const result: string[] = []; + + for (const raw of rawList) { + // replace all separators with dots + const cleaned = raw.replace(/[-/]/g, '.'); + const parts = cleaned.split('.'); + + if (parts.length !== 3) continue; + const [yyyy, mm, dd] = parts.map(Number); + + if (!yyyy || !mm || !dd) continue; + + // validate date + const date = new Date(yyyy, mm - 1, dd); + if (date.getFullYear() !== yyyy || date.getMonth() !== mm - 1 || date.getDate() !== dd) continue; + + const iso = `${yyyy}-${String(mm).padStart(2, '0')}-${String(dd).padStart(2, '0')}`; + + result.push(iso); + } + + return result; + }, + + toAttribute(value: string[] | null): string { + return value ? value.join(',') : ''; + } +}; diff --git a/packages/components/src/components/datepicker/datepicker.ts b/packages/components/src/components/datepicker/datepicker.ts index 7bb240997a..0312340141 100644 --- a/packages/components/src/components/datepicker/datepicker.ts +++ b/packages/components/src/components/datepicker/datepicker.ts @@ -13,6 +13,7 @@ import cx from 'classix'; import SolidElement from '../../internal/solid-element'; import type { SolidFormControl } from '../../internal/solid-element'; import type SdPopup from '../popup/popup'; +import { isoDateConverter, viewMonthConverter, disabledDatesConverter } from './datepicker-converters'; /** * @summary Used to enter or select a date or a range of dates using a calendar view. @@ -80,122 +81,6 @@ import type SdPopup from '../popup/popup'; * @cssproperty --sd-datepicker__date-item--current-color-text - The text color for the current date item. * @cssproperty --sd-datepicker__date-item--selected--hover-color-text - The text color for selected date items in hover state. */ - -const isoDateConverter = { - fromAttribute(value: string | null): string | null { - if (!value) return null; - - // normalize all separators to hyphens - const cleaned = value.trim().replace(/[./]/g, '-'); - - // acccept YYYY-MM-DD only - const match = cleaned.match(/^(\d{4})-(\d{2})-(\d{2})$/); - if (!match) return null; - - const iso = `${match[1]}-${match[2]}-${match[3]}`; - - // validate date - const d = new Date(+match[1], +match[2] - 1, +match[3]); - if (d.getFullYear() !== +match[1] || d.getMonth() !== +match[2] - 1 || d.getDate() !== +match[3]) { - return null; - } - - return iso; - }, - - toAttribute(value: string | null): string { - return value ?? ''; - } -}; - -const viewMonthConverter = { - fromAttribute(value: string | null): Date | null { - if (!value) return null; - - const cleaned = value.trim().replace(/[./]/g, '-'); - let m: RegExpMatchArray | null = cleaned.match(/^(\d{2})-(\d{4})$/); - let month: number; - let year: number; - - if (m) { - month = Number(m[1]); - year = Number(m[2]); - } else { - m = cleaned.match(/^(\d{4})-(\d{2})$/); - if (!m) return null; - year = Number(m[1]); - month = Number(m[2]); - } - - if (!year || !month || month < 1 || month > 12) return null; - - return new Date(year, month - 1, 1); - }, - - toAttribute(value: Date | null): string { - if (!value) return ''; - const year = value.getFullYear(); - const month = String(value.getMonth() + 1).padStart(2, '0'); - return `${year}-${month}`; - } -}; - -const disabledDatesConverter = { - fromAttribute(value: string | null): string[] { - if (!value) return []; - - let rawList: string[] = []; - - const trimmed = value.trim(); - - // arrays - if (trimmed.startsWith('[')) { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const parsed = JSON.parse(trimmed); - if (Array.isArray(parsed)) { - rawList = parsed.map(String); - } - } catch { - return []; - } - } else { - // space separated - rawList = trimmed - .split(/[\s,]+/) - .map(v => v.trim()) - .filter(Boolean); - } - - const result: string[] = []; - - for (const raw of rawList) { - // replace all separators with dots - const cleaned = raw.replace(/[-/]/g, '.'); - const parts = cleaned.split('.'); - - if (parts.length !== 3) continue; - const [yyyy, mm, dd] = parts.map(Number); - - if (!yyyy || !mm || !dd) continue; - - // validate date - const date = new Date(yyyy, mm - 1, dd); - if (date.getFullYear() !== yyyy || date.getMonth() !== mm - 1 || date.getDate() !== dd) continue; - - const iso = `${yyyy}-${String(mm).padStart(2, '0')}-${String(dd).padStart(2, '0')}`; - - result.push(iso); - } - - return result; - }, - - toAttribute(value: string[] | null): string { - return value ? value.join(',') : ''; - } -}; - @customElement('sd-datepicker') export default class SdDatepicker extends SolidElement implements SolidFormControl { /** Localize controller used to fetch localized terms/labels. */