Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/components/calendar/bl-calendar.css
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,23 @@
.calendar-text {
font: var(--bl-font-title-3-regular);
}

.disabled-day-container {
position: relative;
display: inline-block;
}

.custom-day-content {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: auto;
z-index: 1;
}

/* Ensure disabled overlay content visually matches disabled button */
.disabled-day-container .custom-day-content {
color: var(--bl-color-neutral-lighter);
}
39 changes: 39 additions & 0 deletions src/components/calendar/bl-calendar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,5 +667,44 @@ describe("bl-calendar", () => {
expect(strongTag).to.exist;
expect(strongTag?.textContent).to.not.be.empty;
});

it("should render custom day content for disabled dates in special container", async () => {
const dayRendererWithTooltip = (date: Date) => html`<bl-tooltip><span slot="tooltip-trigger">${date.getDate()}</span><div>Custom tooltip</div></bl-tooltip>`;
const disabledDate = new Date(2023, 0, 15);

element = await fixture<BlCalendar>(html`<bl-calendar .dayRenderer=${dayRendererWithTooltip} .disabledDates=${[disabledDate]}></bl-calendar>`);
element._calendarMonth = 0;
element._calendarYear = 2023;
await element.updateComplete;

const dayWrappers = element.shadowRoot?.querySelectorAll(".day-wrapper");
let disabledDayWrapper: Element | null = null;

// Find the disabled day wrapper
dayWrappers?.forEach(wrapper => {
const button = wrapper.querySelector("bl-button");

if (button?.hasAttribute("disabled")) {
const buttonId = button.getAttribute("id");

if (buttonId === disabledDate.getTime().toString()) {
disabledDayWrapper = wrapper;
}
}
});

expect(disabledDayWrapper).to.exist;
const disabledDayContainer = disabledDayWrapper!.querySelector(".disabled-day-container");

expect(disabledDayContainer).to.exist;

const customDayContent = disabledDayContainer!.querySelector(".custom-day-content");

expect(customDayContent).to.exist;

const tooltip = customDayContent!.querySelector("bl-tooltip");

expect(tooltip).to.exist;
});
});
});
21 changes: 21 additions & 0 deletions src/components/calendar/bl-calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,27 @@ export default class BlCalendar extends DatepickerCalendarMixin {
"disabled-day": isDisabledDay,
});

// For disabled days with custom renderer that might contain tooltips,
// wrap in a container that can receive pointer events
if (isDisabledDay && this.dayRenderer) {
return html`<div class="day-wrapper">
<div class="disabled-day-container">
<bl-button
id=${date.getTime()}
variant="tertiary"
kind="neutral"
size="small"
class=${classes}
?disabled=${isDisabledDay}
@click="${() => !isDisabledDay && this.handleDate(date)}"
>
${date.getDate()}
</bl-button>
<div class="custom-day-content">${this.dayRenderer(date)}</div>
</div>
</div>`;
}

return html`<div class="day-wrapper">
<bl-button
id=${date.getTime()}
Expand Down
50 changes: 50 additions & 0 deletions src/components/datepicker/bl-datepicker.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { html } from "lit";
import { ArgsTable, Canvas, Meta, Story } from "@storybook/addon-docs";
import { ifDefined } from "lit/directives/if-defined.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { stringToDateArray } from "../../utilities/string-to-date-converter";

<Meta
title="Components/Datepicker"
Expand Down Expand Up @@ -152,6 +153,55 @@ You can set min date or max date for the datepicker.
</Story>
</Canvas>

### Disabled Dates With Tooltip

You can render tooltips on disabled dates via `dayRenderer`. This example disables specific dates and shows a tooltip explaining why.

<Canvas>
<Story
name="Disabled Dates With Tooltip"
args={{
type: 'single',
label: 'Disabled Dates With Tooltip',
placeholder: 'Select a date',
// Controls: comma-separated dates string
disabledDates: `${new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() + 2)}, ${new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() + 5)}`,
disabledTooltipText: 'This date is disabled',
}}
>
{(args) => {
const disabledDates = typeof args.disabledDates === 'string'
? stringToDateArray(args.disabledDates)
: (args.disabledDates || []);

const isDisabled = (d) => disabledDates.some(x => x.getFullYear() === d.getFullYear() && x.getMonth() === d.getMonth() && x.getDate() === d.getDate());

const dayRenderer = (date) => {
if (isDisabled(date)) {
// Only disabled days have tooltip; trigger is an invisible overlay to avoid duplicating numbers
return html`
<bl-tooltip>
<span slot="tooltip-trigger" style="position:absolute; inset:0; display:block;"></span>
<div>${args.disabledTooltipText}</div>
</bl-tooltip>
`;
}
// Non-disabled days should render the day number normally
return html`${date.getDate()}`;
};

return html`
<bl-datepicker
type=${ifDefined(args.type)}
label=${ifDefined(args.label)}
placeholder=${ifDefined(args.placeholder)}
.disabledDates=${disabledDates}
.dayRenderer=${dayRenderer}
></bl-datepicker>`;
}}
</Story>
</Canvas>

## RTL Support

The datepicker component supports RTL (Right-to-Left) text direction. You can enable RTL mode by setting the `dir` attribute and `lang` attribute on a parent element or the `html` tag.
Expand Down