Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
384 changes: 203 additions & 181 deletions skills/carbon-react/components/date-input.md

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions src/__internal__/__stories__/z-index-interaction.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ export const DateInputInDialog: StoryObj = {
await expect(modal).toHaveAttribute("data-state", "open");
});

const icon = await screen.findByTestId("icon");
await userEvent.click(icon);
const calendarButton = await screen.findByRole("button", {
name: "Open calendar",
});
await userEvent.click(calendarButton);

const datePicker = await screen.findByTestId("date-picker");
expect(datePicker).toBeVisible();
Expand Down Expand Up @@ -458,11 +460,10 @@ export const DateInPopoverContainer: StoryObj = {
play: async () => {
if (!allowInteractions()) return;

const icon = document.querySelector(
'[data-component="icon"][data-element="calendar"]',
);

if (icon) await userEvent.click(icon);
const calendarButton = await screen.findByRole("button", {
name: "Open calendar",
});
await userEvent.click(calendarButton);

const datePicker = await screen.findByTestId("date-picker");
expect(datePicker).toBeVisible();
Expand Down
19 changes: 15 additions & 4 deletions src/__internal__/input/input-style-overrides.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@ import addFocusStyling from "../../style/utils/add-focus-styling";
export const dateStyleOverrides = css`
.date & {
.input-text-container input {
padding: 0 0 0 12px;
margin-right: -12px;
padding-inline-end: 0;
}
}

.date-typical [data-role="date-input-wrapper"] & {
&:focus-within {
box-shadow: none;
outline: none;
}

.input-text-container input:focus {
border-radius: var(--global-radius-action-m) 0 0
var(--global-radius-action-m);
${addFocusStyling()}
z-index: 2;
}
}
`;
Expand Down Expand Up @@ -144,7 +157,6 @@ const searchNewBaseStyles = css`

&:focus-within:has(:focus:not(button)) {
box-shadow: none;
-webkit-box-shadow: none;
}

.input-text-container input[type="search"]:focus {
Expand Down Expand Up @@ -331,7 +343,6 @@ export const passwordStyleOverrides = css`
[data-component="password"] & {
&:focus-within {
box-shadow: none !important;
-webkit-box-shadow: none !important;
outline: none !important;
}

Expand Down
10 changes: 4 additions & 6 deletions src/__internal__/label/label.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,14 @@ const StyledLabel = styled.label<StyledLabelProps>`
${({ $isRequired, $size, $disabled }) =>
$isRequired &&
css`
display: inline-flex;
align-items: center;

::after {
content: "*";
color: ${$disabled
? "var(--input-labelset-label-disabled)"
: "var(--input-labelset-label-required)"};
content: "*";
display: inline-block;
font: ${getFontToken($size)};
margin-left: 4px;
margin-inline-start: 4px;
}
`}

Expand All @@ -65,7 +63,7 @@ const StyledLabel = styled.label<StyledLabelProps>`
.fieldset-required-input & {
::after {
content: "";
margin-left: 0;
margin-inline-start: 0;
}
}
`;
Expand Down
18 changes: 18 additions & 0 deletions src/__internal__/label/label.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ test("should apply medium font to required indicator by default", () => {
);
});

test("should apply small font to label and required indicator", () => {
render(
<Label size="small" isRequired>
Small Required
</Label>,
);

expect(screen.getByText("Small Required")).toHaveStyleRule(
"font",
"var(--global-font-static-comp-medium-s)",
);
expect(screen.getByText("Small Required")).toHaveStyleRule(
"font",
"var(--global-font-static-comp-medium-s)",
{ modifier: "::after" },
);
});

test("should apply disabled colour and set aria-disabled when `disabled` prop is true", () => {
render(
<Label size="medium" disabled>
Expand Down
103 changes: 68 additions & 35 deletions src/components/date-range/date-range-interaction.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,28 @@ export const MinMaxDateRange: Story = {
play: async ({ canvasElement }) => {
if (!allowInteractions()) return;
const canvas = within(canvasElement);
const [startIcon, endIcon] = canvas.getAllByTestId("icon");
const [startCalendarButton, endCalendarButton] = canvas.getAllByRole(
"button",
{ name: "Open calendar" },
);
const startInput = canvas.getByRole("textbox", { name: /start date/i });
const endInput = canvas.getByRole("textbox", { name: /end date/i });

await userEvent.click(startIcon);
await userEvent.keyboard("{PageUp}{End}{Enter}");
await userEvent.click(startCalendarButton);
const dateBeforeMinimum = canvas.getByRole("button", {
name: /Sunday, July 13th, 2025/i,
});
await expect(dateBeforeMinimum).toBeDisabled();
await userEvent.click(dateBeforeMinimum);
await expect(startInput).toHaveValue("17/07/2025");
await userEvent.click(startCalendarButton);

await userEvent.click(endIcon);
await userEvent.keyboard("{PageDown}{Home}{Enter}");
await userEvent.click(endCalendarButton);
const dateAfterMaximum = canvas.getByRole("button", {
name: /Thursday, July 31st, 2025/i,
});
await expect(dateAfterMaximum).toBeDisabled();
await userEvent.click(dateAfterMaximum);
await expect(endInput).toHaveValue("20/07/2025");
},
};
Expand Down Expand Up @@ -158,14 +170,28 @@ export const DisabledDaysDateRange: Story = {
play: async ({ canvasElement }) => {
if (!allowInteractions()) return;
const canvas = within(canvasElement);
const [startIcon, endIcon] = canvas.getAllByTestId("icon");
const [startCalendarButton, endCalendarButton] = canvas.getAllByRole(
"button",
{ name: "Open calendar" },
);
const startInput = canvas.getByRole("textbox", { name: /start date/i });
const endInput = canvas.getByRole("textbox", { name: /end date/i });

await userEvent.click(startIcon);
await userEvent.keyboard("{ArrowLeft}{Enter}");

await userEvent.click(endIcon);
await userEvent.keyboard("{ArrowRight}{ArrowDown}");
await userEvent.click(startCalendarButton);
const disabledRangeDate = canvas.getByRole("button", {
name: /Wednesday, July 16th, 2025/i,
});
await expect(disabledRangeDate).toBeDisabled();
await userEvent.click(disabledRangeDate);
await expect(startInput).toHaveValue("10/07/2025");
await userEvent.click(startCalendarButton);

await userEvent.click(endCalendarButton);
const disabledWeekendDate = canvas.getByRole("button", {
name: /Saturday, July 26th, 2025/i,
});
await expect(disabledWeekendDate).toBeDisabled();
await userEvent.click(disabledWeekendDate);
await expect(endInput).toHaveValue("25/07/2025");
},
};
Expand All @@ -183,20 +209,13 @@ export const FocusStateDateRange: Story = {
if (!allowInteractions()) return;
const canvas = within(canvasElement);

const [startIcon] = canvas.getAllByTestId("icon");
await userEvent.click(startIcon);
const [startCalendarButton] = canvas.getAllByRole("button", {
name: "Open calendar",
});
await userEvent.click(startCalendarButton);

const selectedDayBtn = canvas.getByRole("button", { name: /selected$/i });

const doc = canvasElement.ownerDocument as Document;
for (let i = 0; i < 6 && doc.activeElement !== selectedDayBtn; i += 1) {
await userEvent.tab();
}

if (doc.activeElement !== selectedDayBtn) {
(selectedDayBtn as HTMLElement).focus();
}

await expect(selectedDayBtn).toHaveFocus();
},
};
Expand Down Expand Up @@ -259,6 +278,7 @@ export const LocaleDeDateRange: Story = {
ariaLabels: {
previousMonthButton: () => "de-DE-previous",
nextMonthButton: () => "de-DE-next",
chooseMonth: () => "de-DE-month",
},
},
}}
Expand All @@ -275,12 +295,18 @@ export const LocaleDeDateRange: Story = {
if (!allowInteractions()) return;

const canvas = within(canvasElement);
const [startIcon] = canvas.getAllByTestId("icon");
await userEvent.click(startIcon);
const [startCalendarButton] = canvas.getAllByRole("button", {
name: "Open calendar",
});
await userEvent.click(startCalendarButton);

const prevMonth = canvas.getByRole("button", { name: /de-DE-previous/i });
await userEvent.click(prevMonth);
await expect(prevMonth).toHaveFocus();
const monthSelect = canvas.getByRole("combobox", {
name: /de-DE-month/i,
});
await userEvent.selectOptions(monthSelect, "5");
await expect(monthSelect).toHaveValue("5");
await expect(monthSelect).toHaveDisplayValue("Juni");
await expect(monthSelect).toHaveFocus();
},
};

Expand All @@ -298,8 +324,10 @@ export const TypingSyncDateRange: Story = {

const canvas = within(canvasElement);

const [startIcon] = canvas.getAllByTestId("icon");
await userEvent.click(startIcon);
const [startCalendarButton] = canvas.getAllByRole("button", {
name: "Open calendar",
});
await userEvent.click(startCalendarButton);

const startInput = canvas.getByRole("textbox", { name: /start date/i });
await userEvent.clear(startInput);
Expand All @@ -323,12 +351,17 @@ export const MonthYearNavigationDateRange: Story = {
),
play: async ({ canvasElement }) => {
if (!allowInteractions()) return;
const c = within(canvasElement);
const [startIcon] = c.getAllByTestId("icon");
await userEvent.click(startIcon);
const next = c.getByRole("button", { name: /next month/i });
await userEvent.click(next);
await expect(next).toHaveFocus();
const canvas = within(canvasElement);
const [startCalendarButton] = canvas.getAllByRole("button", {
name: "Open calendar",
});
await userEvent.click(startCalendarButton);
const monthSelect = canvas.getByRole("combobox", {
name: "Choose the month",
});
await userEvent.selectOptions(monthSelect, "7");
await expect(monthSelect).toHaveValue("7");
await expect(monthSelect).toHaveFocus();
},
};
MonthYearNavigationDateRange.parameters = {
Expand Down
2 changes: 1 addition & 1 deletion src/components/date-range/date-range.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
formattedValue,
parseISODate,
checkISOFormatAndLength,
} from "../date/__internal__/utils";
} from "../date/__internal__/utils/utils";
import DateInput, { DateChangeEvent, DateInputProps } from "../date";
import { filterStyledSystemMarginProps } from "../../style/utils";
import tagComponent, {
Expand Down
8 changes: 5 additions & 3 deletions src/components/date-range/date-range.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ You can use the `required` prop to indicate if the fields are mandatory.
<Canvas of={DateRangeStories.Required} />

### With disabled dates
You can disable dates by using `startDateProps` and `endDateProps`, which allow you to pass properties directly to the start or end date.

You can disable dates by using `startDateProps` and `endDateProps`, which allow you to pass properties directly to the start or end date.
For example, you can set a `minDate` or `maxDate`, or use `pickerProps` with a `disabled` option that accepts a matcher or an array of [matchers](https://daypicker.dev/api/type-aliases/Matcher#example).

<Canvas of={DateRangeStories.WithDisabledDates} />

### Locale override

The examples below illustrates how to override the locale passed into the DateRange component. In this example it has been set up for
the French locale. Required locales can be imported like so `import { fr } from 'date-fns/locale';`
The example below illustrates how to override the locale passed into the DateRange component. Locale values are merged with `en-GB`,
so omitted accessibility labels fall back to British English. Applications should provide approved translations through
`I18nProvider` for every language they support. Required date locales can be imported like so `import { fr } from 'date-fns/locale';`

<Canvas of={DateRangeStories.LocaleOverrideExampleImplementation} />

Expand Down
Loading
Loading