uikit-react-public 0.11.24 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/components/Accordion/Accordion.Heading.d.ts +4 -4
- package/dist/components/Accordion/Accordion.Panel.d.ts +2 -2
- package/dist/components/Accordion/Accordion.d.ts +1 -1
- package/dist/components/Accordion/Accordion.stories.d.ts +57 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Avatar/Avatar.stories.d.ts +107 -1
- package/dist/components/Badge/Badge.d.ts +6 -0
- package/dist/components/Badge/Badge.stories.d.ts +15 -0
- package/dist/components/Badge/index.d.ts +2 -0
- package/dist/components/Button/Button.d.ts +3 -1
- package/dist/components/Calendar/index.d.ts +1 -1
- package/dist/components/CookieNotice/CookieNotice.d.ts +16 -0
- package/dist/components/CookieNotice/index.d.ts +2 -0
- package/dist/components/Datepicker/Datepicker.d.ts +1 -1
- package/dist/components/Datepicker/Datepicker.stories.d.ts +4 -3
- package/dist/components/Datepicker/Datepicker.types.d.ts +4 -5
- package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +4 -1
- package/dist/components/Datepicker/subcomponents/DatepickerInput.d.ts +15 -2
- package/dist/components/Datepicker/subcomponents/Panel.d.ts +1 -1
- package/dist/components/Datepicker/subcomponents/VisibleField.d.ts +6 -1
- package/dist/components/Datepicker/subcomponents/index.d.ts +0 -1
- package/dist/components/Datepicker/utils/index.d.ts +0 -1
- package/dist/components/Dialog/BaseDialog.d.ts +8 -2
- package/dist/components/Dialog/Dialog.d.ts +2 -0
- package/dist/components/FileInput/FileInput.d.ts +8 -0
- package/dist/components/FileInput/FileInput.stories.d.ts +16 -0
- package/dist/components/FileInput/index.d.ts +2 -0
- package/dist/components/Header/Header.d.ts +7 -1
- package/dist/components/Header/Header.stories.d.ts +40 -0
- package/dist/components/Heading/Heading.d.ts +1 -1
- package/dist/components/Link/BaseLink.d.ts +10 -0
- package/dist/components/Link/Link.d.ts +5 -10
- package/dist/components/Link/Link.stories.d.ts +1 -1
- package/dist/components/Link/index.d.ts +1 -1
- package/dist/components/Main/Main.d.ts +21 -0
- package/dist/components/Main/Main.stories.d.ts +15 -0
- package/dist/components/Main/__tests__/Main.test.d.ts +1 -0
- package/dist/components/Main/index.d.ts +2 -0
- package/dist/components/Menu/MenuContent.d.ts +1 -1
- package/dist/components/Menu/MenuItem.d.ts +2 -0
- package/dist/components/Menu/MenuSection.d.ts +1 -1
- package/dist/components/NativeDatepicker/NativeDatepicker.d.ts +3 -0
- package/dist/components/NativeDatepicker/NativeDatepicker.stories.d.ts +36 -0
- package/dist/components/NativeDatepicker/NativeDatepicker.types.d.ts +10 -0
- package/dist/components/NativeDatepicker/index.d.ts +2 -0
- package/dist/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.d.ts +1 -1
- package/dist/components/NativeDatepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts +1 -0
- package/dist/components/NativeDatepicker/utils/index.d.ts +1 -0
- package/dist/components/Search/Search.d.ts +16 -0
- package/dist/components/Search/Search.stories.d.ts +34 -0
- package/dist/components/Search/__tests__/Search.test.d.ts +1 -0
- package/dist/components/Search/index.d.ts +2 -0
- package/dist/components/Select/Select.d.ts +1 -1
- package/dist/components/Select/Select.stories.d.ts +157 -9
- package/dist/components/Select/Select.types.d.ts +66 -32
- package/dist/components/Select/subcomponents/CustomOption.d.ts +1 -1
- package/dist/components/Select/subcomponents/CustomSelect.d.ts +3 -3
- package/dist/components/Select/subcomponents/FilterInput.d.ts +14 -0
- package/dist/components/Select/subcomponents/NativeSelect.d.ts +5 -1
- package/dist/components/Select/subcomponents/Panel.d.ts +1 -1
- package/dist/components/Select/subcomponents/VisibleField.d.ts +6 -4
- package/dist/components/Select/subcomponents/index.d.ts +1 -0
- package/dist/components/StandaloneLink/StandaloneLink.d.ts +12 -0
- package/dist/components/StandaloneLink/StandaloneLink.stories.d.ts +13 -0
- package/dist/components/StandaloneLink/__tests__/StandaloneLink.test.d.ts +1 -0
- package/dist/components/StandaloneLink/index.d.ts +2 -0
- package/dist/components/Table/Table.d.ts +10 -8
- package/dist/components/Table/Table.stories.d.ts +21 -0
- package/dist/components/Table/Table.types.d.ts +11 -0
- package/dist/components/Table/__tests__/Table.test.d.ts +1 -0
- package/dist/components/Table/index.d.ts +2 -1
- package/dist/components/Table/subcomponents/Body.d.ts +4 -0
- package/dist/components/Table/subcomponents/Cell/Cell.d.ts +12 -0
- package/dist/components/Table/subcomponents/Cell/Cell.stories.d.ts +313 -0
- package/dist/components/Table/subcomponents/Cell/CellContent.d.ts +10 -0
- package/dist/components/Table/subcomponents/Cell/__tests__/Cell.test.d.ts +1 -0
- package/dist/components/Table/subcomponents/Head.d.ts +4 -0
- package/dist/components/Table/subcomponents/HeadCell/HeadCell.d.ts +13 -0
- package/dist/components/Table/subcomponents/HeadCell/HeadCell.stories.d.ts +312 -0
- package/dist/components/Table/subcomponents/HeadCell/HeadCellContent.d.ts +10 -0
- package/dist/components/Table/subcomponents/HeadCell/__tests__/HeadCell.test.d.ts +1 -0
- package/dist/components/Table/subcomponents/Row.d.ts +5 -0
- package/dist/components/Table/subcomponents/SortIcon.d.ts +7 -0
- package/dist/components/Table/subcomponents/index.d.ts +10 -0
- package/dist/components/Tabs/Tab.d.ts +1 -1
- package/dist/components/Tabs/TabContext.d.ts +1 -0
- package/dist/components/Tabs/Tabs.d.ts +3 -1
- package/dist/components/Tabs/Tabs.stories.d.ts +3 -0
- package/dist/components/Timepicker/Timepicker.d.ts +10 -0
- package/dist/components/Timepicker/Timepicker.stories.d.ts +7 -0
- package/dist/components/Timepicker/__tests__/Timepicker.test.d.ts +1 -0
- package/dist/components/Timepicker/index.d.ts +2 -0
- package/dist/components/Timepicker/utils/convertDateToTimeString.d.ts +2 -0
- package/dist/components/Timepicker/utils/convertDateToTimeString.test.d.ts +1 -0
- package/dist/components/Timepicker/utils/index.d.ts +1 -0
- package/dist/components/WeekPicker/WeekPicker.d.ts +3 -0
- package/dist/components/WeekPicker/WeekPicker.stories.d.ts +41 -0
- package/dist/components/WeekPicker/WeekPicker.types.d.ts +16 -0
- package/dist/components/WeekPicker/index.d.ts +2 -0
- package/dist/components/WeekPicker/subcomponents/CustomDatepicker.d.ts +17 -0
- package/dist/components/WeekPicker/subcomponents/DatepickerInput.d.ts +13 -0
- package/dist/components/WeekPicker/subcomponents/VisibleField.d.ts +15 -0
- package/dist/components/WeekPicker/subcomponents/index.d.ts +3 -0
- package/dist/components/index.d.ts +19 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useFocusTrap.d.ts +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6460 -4607
- package/dist/theme/defaultTheme.d.ts +7 -0
- package/dist/theme/useTheme.d.ts +14 -0
- package/dist/utils/__tests__/announce.test.d.ts +1 -0
- package/dist/utils/__tests__/capitalise.test.d.ts +1 -0
- package/dist/utils/announce.d.ts +6 -0
- package/dist/utils/capitalise.d.ts +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/lib/components/Accordion/Accordion.Heading.tsx +27 -8
- package/lib/components/Accordion/Accordion.Panel.tsx +11 -3
- package/lib/components/Accordion/Accordion.stories.tsx +139 -0
- package/lib/components/Accordion/Accordion.tsx +10 -8
- package/lib/components/Accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +7 -7
- package/lib/components/Accordion/index.ts +2 -0
- package/lib/components/Alert/Alert.stories.tsx +1 -1
- package/lib/components/Alert/Alert.tsx +7 -1
- package/lib/components/Alert/__tests__/__snapshots__/Alert.test.tsx.snap +4 -0
- package/lib/components/Avatar/Avatar.mdx +117 -0
- package/lib/components/Avatar/Avatar.stories.tsx +110 -2
- package/lib/components/Badge/Badge.stories.tsx +19 -0
- package/lib/components/Badge/Badge.tsx +48 -0
- package/lib/components/Badge/index.ts +2 -0
- package/lib/components/Blanket/Blanket.stories.tsx +1 -1
- package/lib/components/Breadcrumbs/__tests__/__snapshots__/Breadcrumbs.test.tsx.snap +4 -4
- package/lib/components/Button/Button.stories.tsx +1 -1
- package/lib/components/Button/Button.tsx +6 -2
- package/lib/components/Calendar/Calendar.stories.tsx +12 -32
- package/lib/components/Calendar/__tests__/Calendar.test.tsx +23 -15
- package/lib/components/Calendar/index.ts +1 -5
- package/lib/components/Calendar/subcomponents/AcademicWeeks.tsx +2 -1
- package/lib/components/Calendar/subcomponents/ColumnHeading.tsx +5 -1
- package/lib/components/Calendar/subcomponents/EventDot.tsx +2 -1
- package/lib/components/Calendar/subcomponents/Grid.tsx +0 -1
- package/lib/components/Calendar/subcomponents/index.ts +1 -1
- package/lib/components/Calendar/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.ts +43 -11
- package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.test.ts +5 -5
- package/lib/components/CookieNotice/CookieNotice.tsx +114 -0
- package/lib/components/CookieNotice/index.ts +2 -0
- package/lib/components/Datepicker/Datepicker.lld.md +108 -0
- package/lib/components/Datepicker/Datepicker.stories.tsx +44 -5
- package/lib/components/Datepicker/Datepicker.tsx +14 -36
- package/lib/components/Datepicker/Datepicker.types.ts +5 -14
- package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +150 -8
- package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +10 -4
- package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +39 -5
- package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +30 -17
- package/lib/components/Datepicker/subcomponents/Panel.tsx +6 -2
- package/lib/components/Datepicker/subcomponents/VisibleField.tsx +40 -3
- package/lib/components/Datepicker/subcomponents/index.ts +0 -1
- package/lib/components/Datepicker/utils/index.ts +0 -1
- package/lib/components/Dialog/BaseDialog.tsx +55 -4
- package/lib/components/Dialog/Dialog.tsx +8 -1
- package/lib/components/Dialog/DialogBody.tsx +5 -1
- package/lib/components/Dialog/DialogHeader.tsx +2 -1
- package/lib/components/Divider/Divider.stories.tsx +1 -1
- package/lib/components/Field/ErrorText.tsx +1 -0
- package/lib/components/Field/Field.stories.tsx +1 -1
- package/lib/components/Field/__tests__/Field.test.tsx +161 -148
- package/lib/components/FileInput/FileInput.stories.tsx +70 -0
- package/lib/components/FileInput/FileInput.tsx +68 -0
- package/lib/components/FileInput/__tests__/FileInput.test.tsx +99 -0
- package/lib/components/FileInput/__tests__/__snapshots__/FileInput.test.tsx.snap +91 -0
- package/lib/components/FileInput/index.ts +2 -0
- package/lib/components/Footer/Footer.stories.tsx +1 -1
- package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +28 -28
- package/lib/components/Header/Header.mdx +52 -0
- package/lib/components/Header/Header.stories.tsx +98 -0
- package/lib/components/Header/Header.tsx +65 -3
- package/lib/components/Header/__tests__/Header.test.tsx +17 -1
- package/lib/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +4 -4
- package/lib/components/Heading/Documentation.mdx +1 -1
- package/lib/components/Heading/Heading.stories.tsx +1 -1
- package/lib/components/Heading/Heading.tsx +1 -1
- package/lib/components/Heading/__tests__/Heading.test.tsx +7 -19
- package/lib/components/Heading/__tests__/__snapshots__/Heading.test.tsx.snap +7 -7
- package/lib/components/Icon/Icon.stories.tsx +1 -1
- package/lib/components/IconButton/IconButton.stories.tsx +1 -1
- package/lib/components/Input/Input.stories.tsx +1 -1
- package/lib/components/Label/Label.stories.tsx +1 -1
- package/lib/components/Label/Label.tsx +0 -2
- package/lib/components/Label/__tests__/__snapshots__/Label.test.tsx.snap +7 -7
- package/lib/components/Link/BaseLink.tsx +84 -0
- package/lib/components/Link/Link.tsx +72 -32
- package/lib/components/Link/__tests__/__snapshots__/link.test.tsx.snap +3 -3
- package/lib/components/Link/__tests__/link.test.tsx +6 -13
- package/lib/components/Link/index.ts +1 -1
- package/lib/components/Main/Main.stories.tsx +36 -0
- package/lib/components/Main/Main.tsx +46 -0
- package/lib/components/Main/__tests__/Main.test.tsx +80 -0
- package/lib/components/Main/__tests__/__snapshots__/Main.test.tsx.snap +33 -0
- package/lib/components/Main/index.ts +2 -0
- package/lib/components/Menu/Menu.context.tsx +3 -1
- package/lib/components/Menu/Menu.tsx +2 -2
- package/lib/components/Menu/MenuContent.tsx +5 -5
- package/lib/components/Menu/MenuItem.tsx +20 -3
- package/lib/components/Menu/MenuSection.tsx +4 -3
- package/lib/components/NativeDatepicker/NativeDatepicker.stories.tsx +100 -0
- package/lib/components/{Datepicker/subcomponents → NativeDatepicker}/NativeDatepicker.tsx +14 -15
- package/lib/components/NativeDatepicker/NativeDatepicker.types.ts +19 -0
- package/lib/components/NativeDatepicker/index.ts +2 -0
- package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.ts +1 -1
- package/lib/components/NativeDatepicker/utils/index.ts +1 -0
- package/lib/components/Pagination/PaginationControls.tsx +56 -15
- package/lib/components/Pagination/PaginationInfo.tsx +5 -1
- package/lib/components/Paragraph/Paragraph.stories.tsx +1 -1
- package/lib/components/Search/Search.stories.tsx +41 -0
- package/lib/components/Search/Search.tsx +170 -0
- package/lib/components/Search/__tests__/Search.test.tsx +112 -0
- package/lib/components/Search/__tests__/__snapshots__/Search.test.tsx.snap +179 -0
- package/lib/components/Search/index.ts +2 -0
- package/lib/components/Select/Select.mdx +169 -0
- package/lib/components/Select/Select.stories.tsx +198 -77
- package/lib/components/Select/Select.tsx +37 -13
- package/lib/components/Select/Select.types.ts +77 -54
- package/lib/components/Select/__tests__/Select.test.tsx +448 -7
- package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +3 -3
- package/lib/components/Select/subcomponents/CustomOption.tsx +24 -10
- package/lib/components/Select/subcomponents/CustomSelect.tsx +333 -52
- package/lib/components/Select/subcomponents/FilterInput.tsx +80 -0
- package/lib/components/Select/subcomponents/NativeSelect.tsx +13 -1
- package/lib/components/Select/subcomponents/Panel.tsx +4 -5
- package/lib/components/Select/subcomponents/VisibleField.tsx +36 -24
- package/lib/components/Select/subcomponents/index.tsx +1 -0
- package/lib/components/Snackbar/Snackbar.stories.tsx +1 -1
- package/lib/components/Spinner/Spinner.stories.tsx +1 -1
- package/lib/components/StandaloneLink/StandaloneLink.stories.tsx +32 -0
- package/lib/components/StandaloneLink/StandaloneLink.tsx +183 -0
- package/lib/components/StandaloneLink/__tests__/StandaloneLink.test.tsx +57 -0
- package/lib/components/StandaloneLink/__tests__/__snapshots__/StandaloneLink.test.tsx.snap +19 -0
- package/lib/components/StandaloneLink/index.ts +2 -0
- package/lib/components/Table/Table.stories.tsx +337 -0
- package/lib/components/Table/Table.tsx +42 -67
- package/lib/components/Table/Table.types.ts +14 -0
- package/lib/components/Table/__tests__/Table.test.tsx +121 -0
- package/lib/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +210 -0
- package/lib/components/Table/index.ts +8 -1
- package/lib/components/Table/subcomponents/Body.tsx +18 -0
- package/lib/components/Table/subcomponents/Cell/Cell.stories.tsx +151 -0
- package/lib/components/Table/subcomponents/Cell/Cell.tsx +72 -0
- package/lib/components/Table/subcomponents/Cell/CellContent.tsx +91 -0
- package/lib/components/Table/subcomponents/Cell/__tests__/Cell.test.tsx +115 -0
- package/lib/components/Table/subcomponents/Cell/__tests__/__snapshots__/Cell.test.tsx.snap +107 -0
- package/lib/components/Table/subcomponents/Head.tsx +34 -0
- package/lib/components/Table/subcomponents/HeadCell/HeadCell.stories.tsx +85 -0
- package/lib/components/Table/subcomponents/HeadCell/HeadCell.tsx +99 -0
- package/lib/components/Table/subcomponents/HeadCell/HeadCellContent.tsx +61 -0
- package/lib/components/Table/subcomponents/HeadCell/__tests__/HeadCell.test.tsx +137 -0
- package/lib/components/Table/subcomponents/HeadCell/__tests__/__snapshots__/HeadCell.test.tsx.snap +110 -0
- package/lib/components/Table/subcomponents/Row.tsx +49 -0
- package/lib/components/Table/subcomponents/SortIcon.tsx +63 -0
- package/lib/components/Table/subcomponents/index.ts +14 -0
- package/lib/components/Tabs/Tab.tsx +3 -3
- package/lib/components/Tabs/TabContext.tsx +1 -0
- package/lib/components/Tabs/Tabs.stories.tsx +9 -3
- package/lib/components/Tabs/Tabs.tsx +10 -32
- package/lib/components/Tabs/__tests__/Tabs.test.tsx +10 -4
- package/lib/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +32 -32
- package/lib/components/Textarea/Textarea.stories.tsx +1 -1
- package/lib/components/Timepicker/Timepicker.stories.tsx +43 -0
- package/lib/components/Timepicker/Timepicker.tsx +100 -0
- package/lib/components/Timepicker/__tests__/Timepicker.test.tsx +55 -0
- package/lib/components/Timepicker/__tests__/__snapshots__/Timepicker.test.tsx.snap +19 -0
- package/lib/components/Timepicker/index.tsx +2 -0
- package/lib/components/Timepicker/utils/convertDateToTimeString.test.ts +54 -0
- package/lib/components/Timepicker/utils/convertDateToTimeString.ts +10 -0
- package/lib/components/Timepicker/utils/index.ts +1 -0
- package/lib/components/Toggle/Toggle.stories.tsx +1 -1
- package/lib/components/Tooltip/Tooltip.stories.tsx +1 -1
- package/lib/components/WeekPicker/WeekPicker.stories.tsx +147 -0
- package/lib/components/WeekPicker/WeekPicker.tsx +26 -0
- package/lib/components/WeekPicker/WeekPicker.types.ts +21 -0
- package/lib/components/WeekPicker/index.ts +2 -0
- package/lib/components/WeekPicker/subcomponents/CustomDatepicker.tsx +298 -0
- package/lib/components/WeekPicker/subcomponents/DatepickerInput.tsx +111 -0
- package/lib/components/WeekPicker/subcomponents/VisibleField.tsx +126 -0
- package/lib/components/WeekPicker/subcomponents/index.ts +3 -0
- package/lib/components/common/Common.mdx +1 -1
- package/lib/components/index.ts +28 -2
- package/lib/hooks/index.ts +2 -0
- package/lib/hooks/useFocusTrap.ts +159 -0
- package/lib/index.ts +2 -0
- package/lib/theme/defaultTheme.ts +7 -0
- package/lib/utils/__tests__/announce.test.ts +121 -0
- package/lib/utils/__tests__/capitalise.test.ts +40 -0
- package/lib/utils/announce.ts +134 -0
- package/lib/utils/capitalise.ts +4 -0
- package/lib/utils/index.ts +1 -0
- package/package.json +3 -6
- package/dist/components/Datepicker/subcomponents/NativeDatepicker.d.ts +0 -6
- package/lib/components/Accordion/Accordion.stories.tsx.NOT_READY +0 -93
- package/lib/components/Field/__tests__/__snapshots__/Field.test.tsx.snap +0 -300
- /package/dist/components/{Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts → FileInput/__tests__/FileInput.test.d.ts} +0 -0
- /package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.test.ts +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { css, cx } from '@emotion/css';
|
|
2
|
+
import { DatepickerInput } from './';
|
|
3
|
+
import { Icon } from '../../..';
|
|
4
|
+
import { useTheme } from '../../../theme';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface VisibleFieldProps {
|
|
8
|
+
inputValue: string;
|
|
9
|
+
onInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
10
|
+
onInputKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
11
|
+
onInputFocus: () => void;
|
|
12
|
+
onButtonClick: () => void;
|
|
13
|
+
disabled: boolean;
|
|
14
|
+
inputRef: React.RefObject<HTMLInputElement | null>;
|
|
15
|
+
beginningOfWeek?: string;
|
|
16
|
+
endOfWeek?: string;
|
|
17
|
+
academicWeekNumber?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NAME = 'ucl-uikit-weekpicker__visible-field';
|
|
21
|
+
|
|
22
|
+
const VisibleField = ({
|
|
23
|
+
inputValue,
|
|
24
|
+
onInputChange,
|
|
25
|
+
onInputKeyDown,
|
|
26
|
+
onInputFocus,
|
|
27
|
+
onButtonClick,
|
|
28
|
+
disabled,
|
|
29
|
+
inputRef,
|
|
30
|
+
beginningOfWeek,
|
|
31
|
+
endOfWeek,
|
|
32
|
+
academicWeekNumber,
|
|
33
|
+
}: VisibleFieldProps) => {
|
|
34
|
+
const [theme] = useTheme();
|
|
35
|
+
|
|
36
|
+
const baseStyle = css`
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
// width: 100%;
|
|
41
|
+
// height: 100%;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
border: 1px solid ${theme.color.neutral.grey60};
|
|
44
|
+
background-color: ${theme.color.neutral.white};
|
|
45
|
+
padding: 3px 16px;
|
|
46
|
+
|
|
47
|
+
&:focus-within {
|
|
48
|
+
box-shadow: ${theme.boxShadow.focus};
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const disabledStyle = css`
|
|
53
|
+
color: ${theme.color.text.disabled};
|
|
54
|
+
background-color: ${theme.color.neutral.white};
|
|
55
|
+
border-color: ${theme.color.neutral.grey20};
|
|
56
|
+
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
// And child elements
|
|
59
|
+
& * {
|
|
60
|
+
cursor: not-allowed;
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
// The container for the <Icon.Calendar> is a <div> not an <IconButton>,
|
|
65
|
+
// so that the orange border only appears when the input has focus and the user can type.
|
|
66
|
+
// The container increases the clickable area of the icon.
|
|
67
|
+
const iconButtonStyle = css`
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
height: 100%;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
padding-right: 8px;
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
// `min-width` here accounts for a recurring problem,
|
|
77
|
+
// in which icons shrink when horizontal space is limited.
|
|
78
|
+
// TODO: This ought to be fixed in the <Icon> component itself.
|
|
79
|
+
const iconStyle = css`
|
|
80
|
+
min-width: 16px;
|
|
81
|
+
color: ${disabled
|
|
82
|
+
? theme.color.text.disabled
|
|
83
|
+
: theme.color.text.primary}; // TODO: Needs a design token
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const style = cx(NAME, baseStyle, disabled && disabledStyle);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div
|
|
90
|
+
className={style}
|
|
91
|
+
data-testid={NAME}
|
|
92
|
+
>
|
|
93
|
+
<div
|
|
94
|
+
onClick={onButtonClick}
|
|
95
|
+
className={iconButtonStyle}
|
|
96
|
+
>
|
|
97
|
+
<Icon.Calendar
|
|
98
|
+
className={iconStyle}
|
|
99
|
+
size={16}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
<DatepickerInput
|
|
103
|
+
value={inputValue}
|
|
104
|
+
onChange={onInputChange}
|
|
105
|
+
onKeyDown={onInputKeyDown}
|
|
106
|
+
onFocus={onInputFocus}
|
|
107
|
+
disabled={disabled}
|
|
108
|
+
ref={inputRef}
|
|
109
|
+
beginningOfWeek={beginningOfWeek}
|
|
110
|
+
endOfWeek={endOfWeek}
|
|
111
|
+
academicWeekNumber={academicWeekNumber}
|
|
112
|
+
/>
|
|
113
|
+
{academicWeekNumber !== undefined && (
|
|
114
|
+
<span
|
|
115
|
+
className={css`
|
|
116
|
+
color: ${theme.color.text.secondary};
|
|
117
|
+
`}
|
|
118
|
+
>
|
|
119
|
+
W{academicWeekNumber}
|
|
120
|
+
</span>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default VisibleField;
|
|
@@ -76,7 +76,7 @@ The available margin props are:
|
|
|
76
76
|
include={['m', 'm', 'mv', 'mh', 'mt', 'mb', 'ml', 'mr', 'noMargins']}
|
|
77
77
|
/>
|
|
78
78
|
|
|
79
|
-
[A live example of common margin props can be found in this `<Button>` story.](?path=/story/components-
|
|
79
|
+
[A live example of common margin props can be found in this `<Button>` story.](?path=/story/components-button--common-margins)
|
|
80
80
|
|
|
81
81
|
Implementation details can be found in <a href='https://github.com/ucl-isd/uikit-react/blob/main/lib/components/common/marginsStyle.ts' target='_blank'>uikit-react/components/common/MarginsStyle.tsx</a>
|
|
82
82
|
|
package/lib/components/index.ts
CHANGED
|
@@ -7,6 +7,9 @@ export type { InputProps } from './Input';
|
|
|
7
7
|
export { default as Link } from './Link';
|
|
8
8
|
export type { LinkProps } from './Link';
|
|
9
9
|
|
|
10
|
+
export { default as StandaloneLink } from './StandaloneLink';
|
|
11
|
+
export type { StandaloneLinkProps } from './StandaloneLink';
|
|
12
|
+
|
|
10
13
|
export { default as Button } from './Button';
|
|
11
14
|
export type { ButtonProps } from './Button';
|
|
12
15
|
|
|
@@ -100,8 +103,8 @@ export type { TabsProps } from './Tabs';
|
|
|
100
103
|
export type { TabProps } from './Tabs/Tab';
|
|
101
104
|
|
|
102
105
|
// todo:
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
export { default as Accordion } from './Accordion';
|
|
107
|
+
export type { AccordionProps } from './Accordion';
|
|
105
108
|
|
|
106
109
|
export { default as Field } from './Field';
|
|
107
110
|
export type { FieldProps } from './Field';
|
|
@@ -131,5 +134,28 @@ export type { RadioProps, LabelledRadioProps } from './Radio';
|
|
|
131
134
|
export { default as Datepicker } from './Datepicker';
|
|
132
135
|
export type { DatepickerProps } from './Datepicker';
|
|
133
136
|
|
|
137
|
+
export { default as NativeDatepicker } from './NativeDatepicker';
|
|
138
|
+
export type { NativeDatepickerProps } from './NativeDatepicker';
|
|
139
|
+
|
|
134
140
|
export { default as Calendar } from './Calendar';
|
|
135
141
|
export type { CalendarProps } from './Calendar';
|
|
142
|
+
|
|
143
|
+
export { default as WeekPicker } from './WeekPicker';
|
|
144
|
+
|
|
145
|
+
export { default as FileInput } from './FileInput';
|
|
146
|
+
export type { FileInputProps } from './FileInput';
|
|
147
|
+
|
|
148
|
+
export { default as Timepicker } from './Timepicker';
|
|
149
|
+
export type { TimepickerProps } from './Timepicker';
|
|
150
|
+
|
|
151
|
+
export { default as CookieNotice } from './CookieNotice';
|
|
152
|
+
export type { CookieNoticeProps } from './CookieNotice';
|
|
153
|
+
|
|
154
|
+
export { default as Search } from './Search';
|
|
155
|
+
export type { SearchProps } from './Search';
|
|
156
|
+
|
|
157
|
+
export { default as Layout } from './Layout';
|
|
158
|
+
export type { LayoutProps } from './Layout';
|
|
159
|
+
|
|
160
|
+
export { default as Main } from './Main';
|
|
161
|
+
export type { MainProps } from './Main';
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Generated with Claude Sonnet 4
|
|
2
|
+
|
|
3
|
+
import { useEffect, useCallback, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
// Selector for focusable elements
|
|
6
|
+
const FOCUSABLE_ELEMENTS = [
|
|
7
|
+
'a[href]',
|
|
8
|
+
'button:not([disabled])',
|
|
9
|
+
'textarea:not([disabled])',
|
|
10
|
+
'input:not([disabled])',
|
|
11
|
+
'select:not([disabled])',
|
|
12
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
13
|
+
'[contenteditable="true"]',
|
|
14
|
+
'audio[controls]',
|
|
15
|
+
'video[controls]',
|
|
16
|
+
'details > summary:first-of-type',
|
|
17
|
+
].join(', ');
|
|
18
|
+
|
|
19
|
+
interface UseFocusTrapOptions {
|
|
20
|
+
isActive: boolean;
|
|
21
|
+
containerRef: React.RefObject<HTMLElement | null>;
|
|
22
|
+
initialFocusRef?: React.RefObject<HTMLElement>;
|
|
23
|
+
finalFocusRef?: React.RefObject<HTMLElement>;
|
|
24
|
+
restoreFocus?: boolean;
|
|
25
|
+
skipFirstFocusable?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const useFocusTrap = ({
|
|
29
|
+
isActive,
|
|
30
|
+
containerRef,
|
|
31
|
+
initialFocusRef,
|
|
32
|
+
finalFocusRef,
|
|
33
|
+
restoreFocus = true,
|
|
34
|
+
skipFirstFocusable = false,
|
|
35
|
+
}: UseFocusTrapOptions) => {
|
|
36
|
+
const previousActiveElement = useRef<HTMLElement | null>(null);
|
|
37
|
+
|
|
38
|
+
// Get all focusable elements within the container
|
|
39
|
+
const getFocusableElements = useCallback((): HTMLElement[] => {
|
|
40
|
+
if (!containerRef.current) return [];
|
|
41
|
+
|
|
42
|
+
const elements = containerRef.current.querySelectorAll(FOCUSABLE_ELEMENTS);
|
|
43
|
+
return Array.from(elements) as HTMLElement[];
|
|
44
|
+
}, [containerRef]);
|
|
45
|
+
|
|
46
|
+
// Handle tab key navigation
|
|
47
|
+
const handleKeyDown = useCallback(
|
|
48
|
+
(event: KeyboardEvent) => {
|
|
49
|
+
if (!isActive || event.key !== 'Tab') return;
|
|
50
|
+
|
|
51
|
+
const focusableElements = getFocusableElements();
|
|
52
|
+
if (focusableElements.length === 0) {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const firstElement = focusableElements[0];
|
|
58
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
59
|
+
const currentFocus = document.activeElement as HTMLElement;
|
|
60
|
+
|
|
61
|
+
if (event.shiftKey) {
|
|
62
|
+
// Shift + Tab (moving backwards)
|
|
63
|
+
if (
|
|
64
|
+
currentFocus === firstElement ||
|
|
65
|
+
!containerRef.current?.contains(currentFocus)
|
|
66
|
+
) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
lastElement.focus();
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
// Tab (moving forwards)
|
|
72
|
+
if (
|
|
73
|
+
currentFocus === lastElement ||
|
|
74
|
+
!containerRef.current?.contains(currentFocus)
|
|
75
|
+
) {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
firstElement.focus();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[isActive, getFocusableElements, containerRef]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Set initial focus when trap becomes active
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!isActive) return;
|
|
87
|
+
|
|
88
|
+
// Store the previously focused element
|
|
89
|
+
previousActiveElement.current = document.activeElement as HTMLElement;
|
|
90
|
+
|
|
91
|
+
// Focus the specified initial element or the first focusable element
|
|
92
|
+
const focusableElements = getFocusableElements();
|
|
93
|
+
const defaultIndex =
|
|
94
|
+
skipFirstFocusable && focusableElements.length > 1 ? 1 : 0;
|
|
95
|
+
const focusElement =
|
|
96
|
+
initialFocusRef?.current || focusableElements[defaultIndex];
|
|
97
|
+
|
|
98
|
+
let requestAnimationFrameId: number | null = null;
|
|
99
|
+
let setTimeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
100
|
+
|
|
101
|
+
if (focusElement) {
|
|
102
|
+
// Use requestAnimationFrame if available, otherwise setTimeout
|
|
103
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
104
|
+
requestAnimationFrameId = requestAnimationFrame(() => {
|
|
105
|
+
focusElement.focus();
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
setTimeoutId = setTimeout(() => {
|
|
109
|
+
focusElement.focus();
|
|
110
|
+
}, 0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add event listener for keyboard navigation
|
|
115
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
116
|
+
|
|
117
|
+
return () => {
|
|
118
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
119
|
+
if (
|
|
120
|
+
typeof requestAnimationFrame === 'function' &&
|
|
121
|
+
requestAnimationFrameId !== null
|
|
122
|
+
) {
|
|
123
|
+
cancelAnimationFrame(requestAnimationFrameId);
|
|
124
|
+
}
|
|
125
|
+
if (
|
|
126
|
+
typeof requestAnimationFrame !== 'function' &&
|
|
127
|
+
setTimeoutId !== undefined
|
|
128
|
+
) {
|
|
129
|
+
clearTimeout(setTimeoutId);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}, [
|
|
133
|
+
isActive,
|
|
134
|
+
initialFocusRef,
|
|
135
|
+
getFocusableElements,
|
|
136
|
+
handleKeyDown,
|
|
137
|
+
skipFirstFocusable,
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
// Restore focus when trap becomes inactive
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (isActive) return; // Only run when becoming inactive
|
|
143
|
+
|
|
144
|
+
if (restoreFocus && previousActiveElement.current) {
|
|
145
|
+
// Use setTimeout to ensure the dialog is fully closed before restoring focus
|
|
146
|
+
const restoreTimeout = setTimeout(() => {
|
|
147
|
+
const elementToFocus =
|
|
148
|
+
finalFocusRef?.current || previousActiveElement.current;
|
|
149
|
+
if (elementToFocus && document.contains(elementToFocus)) {
|
|
150
|
+
elementToFocus.focus();
|
|
151
|
+
}
|
|
152
|
+
// Clear the stored element after restoring focus
|
|
153
|
+
previousActiveElement.current = null;
|
|
154
|
+
}, 100); // Longer delay for native dialog close
|
|
155
|
+
|
|
156
|
+
return () => clearTimeout(restoreTimeout);
|
|
157
|
+
}
|
|
158
|
+
}, [isActive, finalFocusRef, restoreFocus]);
|
|
159
|
+
};
|
package/lib/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ const pink20 = '#DEB8C3'; // UCL vibrant pink
|
|
|
47
47
|
const black100 = '#000000';
|
|
48
48
|
const black90 = '#1A1A1A';
|
|
49
49
|
const black80 = '#333333';
|
|
50
|
+
const black70 = '#4D4D4D';
|
|
50
51
|
const black60 = '#666666';
|
|
51
52
|
const black40 = '#999999';
|
|
52
53
|
const black20 = '#CCCCCC';
|
|
@@ -102,6 +103,7 @@ const baseColour = {
|
|
|
102
103
|
black100,
|
|
103
104
|
black90,
|
|
104
105
|
black80,
|
|
106
|
+
black70,
|
|
105
107
|
black60,
|
|
106
108
|
black40,
|
|
107
109
|
black20,
|
|
@@ -189,7 +191,9 @@ const systemColours = {
|
|
|
189
191
|
|
|
190
192
|
const linkColours = {
|
|
191
193
|
default: baseColour.blue70,
|
|
194
|
+
secondaryDefault: baseColour.black90,
|
|
192
195
|
hover: baseColour.blue100,
|
|
196
|
+
secondaryHover: baseColour.black70,
|
|
193
197
|
visited: baseColour.purple40,
|
|
194
198
|
disabled: baseColour.black20,
|
|
195
199
|
};
|
|
@@ -203,6 +207,7 @@ const neutralColours = {
|
|
|
203
207
|
black: blackAndWhite.black,
|
|
204
208
|
grey90: baseColour.black90,
|
|
205
209
|
grey80: baseColour.black80,
|
|
210
|
+
grey70: baseColour.black70,
|
|
206
211
|
grey60: baseColour.black60,
|
|
207
212
|
grey40: baseColour.black40,
|
|
208
213
|
grey20: baseColour.black20,
|
|
@@ -253,6 +258,7 @@ const baseSizes = {
|
|
|
253
258
|
};
|
|
254
259
|
|
|
255
260
|
const margin = {
|
|
261
|
+
m0: 0,
|
|
256
262
|
m2: baseSizes.s2,
|
|
257
263
|
m4: baseSizes.s4,
|
|
258
264
|
m6: baseSizes.s6,
|
|
@@ -271,6 +277,7 @@ const margin = {
|
|
|
271
277
|
};
|
|
272
278
|
|
|
273
279
|
const padding = {
|
|
280
|
+
p0: 0,
|
|
274
281
|
p2: baseSizes.s2,
|
|
275
282
|
p4: baseSizes.s4,
|
|
276
283
|
p6: baseSizes.s6,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import announce, { __resetForTesting } from '../announce';
|
|
4
|
+
|
|
5
|
+
describe('announce (ARIA live region)', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
// Reset DOM before each test
|
|
8
|
+
document.body.innerHTML = '';
|
|
9
|
+
__resetForTesting();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('creates a live region on first announce', async () => {
|
|
17
|
+
announce('Hello world');
|
|
18
|
+
|
|
19
|
+
const region = await screen.findByTestId('aria-live-region');
|
|
20
|
+
|
|
21
|
+
expect(region).toBeTruthy();
|
|
22
|
+
expect(region.getAttribute('aria-live')).toBe('polite');
|
|
23
|
+
expect(region.getAttribute('aria-atomic')).toBe('true');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('visually hides the live region but keeps it accessible', async () => {
|
|
27
|
+
announce('Hidden message');
|
|
28
|
+
|
|
29
|
+
const region = await screen.findByTestId('aria-live-region');
|
|
30
|
+
|
|
31
|
+
expect(region.style.position).toBe('absolute');
|
|
32
|
+
expect(region.style.left).toBe('-9999px');
|
|
33
|
+
expect(region.style.width).toBe('1px');
|
|
34
|
+
expect(region.style.height).toBe('1px');
|
|
35
|
+
expect(region.style.overflow).toBe('hidden');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('clears text and sets message after timeout', async () => {
|
|
39
|
+
announce('This is an announcement');
|
|
40
|
+
|
|
41
|
+
const region = await screen.findByTestId('aria-live-region');
|
|
42
|
+
|
|
43
|
+
await waitFor(() => {
|
|
44
|
+
expect(region.textContent).toBe('This is an announcement');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('reuses the same live region on multiple announcements', async () => {
|
|
49
|
+
announce('First message');
|
|
50
|
+
|
|
51
|
+
const region = await screen.findByTestId('aria-live-region');
|
|
52
|
+
|
|
53
|
+
expect(region).toBeTruthy();
|
|
54
|
+
|
|
55
|
+
announce('Second message');
|
|
56
|
+
|
|
57
|
+
// Query all instances
|
|
58
|
+
const allRegions = await screen.findAllByTestId('aria-live-region');
|
|
59
|
+
|
|
60
|
+
expect(allRegions.length).toBe(1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('queues multiple announcements and processes sequentially', async () => {
|
|
64
|
+
vi.useFakeTimers();
|
|
65
|
+
|
|
66
|
+
announce('First message');
|
|
67
|
+
announce('Second message');
|
|
68
|
+
|
|
69
|
+
// Query the live region
|
|
70
|
+
const region = document.querySelector(
|
|
71
|
+
'[data-testid="aria-live-region"]'
|
|
72
|
+
) as HTMLElement;
|
|
73
|
+
|
|
74
|
+
// Initially empty
|
|
75
|
+
expect(region.textContent).toBe('');
|
|
76
|
+
|
|
77
|
+
// After 100ms, first message appears
|
|
78
|
+
vi.advanceTimersByTime(100);
|
|
79
|
+
expect(region.textContent).toBe('First message');
|
|
80
|
+
|
|
81
|
+
// Second message should NOT appear yet (need to wait 1000ms + 100ms more)
|
|
82
|
+
vi.advanceTimersByTime(500);
|
|
83
|
+
expect(region.textContent).toBe('First message');
|
|
84
|
+
|
|
85
|
+
// After remaining 500ms of the 1000ms delay, queue processes but text is cleared
|
|
86
|
+
vi.advanceTimersByTime(500);
|
|
87
|
+
expect(region.textContent).toBe('');
|
|
88
|
+
|
|
89
|
+
// After another 100ms, second message appears
|
|
90
|
+
vi.advanceTimersByTime(100);
|
|
91
|
+
expect(region.textContent).toBe('Second message');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('duplicate prevention while currently announcing', async () => {
|
|
95
|
+
vi.useFakeTimers();
|
|
96
|
+
|
|
97
|
+
announce('Processing');
|
|
98
|
+
announce('Processing'); // Duplicate - should be ignored
|
|
99
|
+
announce('Processing'); // Duplicate - should be ignored
|
|
100
|
+
announce('Done'); // Different message - should be queued
|
|
101
|
+
|
|
102
|
+
const region = document.querySelector(
|
|
103
|
+
'[data-testid="aria-live-region"]'
|
|
104
|
+
) as HTMLElement;
|
|
105
|
+
|
|
106
|
+
// First message appears
|
|
107
|
+
vi.advanceTimersByTime(100);
|
|
108
|
+
expect(region.textContent).toBe('Processing');
|
|
109
|
+
|
|
110
|
+
// Complete first message, next should be "Done" (duplicates were ignored)
|
|
111
|
+
vi.advanceTimersByTime(1000);
|
|
112
|
+
vi.advanceTimersByTime(100);
|
|
113
|
+
expect(region.textContent).toBe('Done');
|
|
114
|
+
|
|
115
|
+
// No more messages in queue
|
|
116
|
+
vi.advanceTimersByTime(1000);
|
|
117
|
+
vi.advanceTimersByTime(100);
|
|
118
|
+
// Still "Done" - nothing else was queued
|
|
119
|
+
expect(region.textContent).toBe('Done');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import capitalise from '../capitalise';
|
|
3
|
+
|
|
4
|
+
describe('capitalise', () => {
|
|
5
|
+
it('should capitalize the first letter of a lowercase word', () => {
|
|
6
|
+
expect(capitalise('hello')).toBe('Hello');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should capitalize the first letter of a sentence', () => {
|
|
10
|
+
expect(capitalise('hello world')).toBe('Hello world');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should handle already capitalized text', () => {
|
|
14
|
+
expect(capitalise('Hello')).toBe('Hello');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle single character strings', () => {
|
|
18
|
+
expect(capitalise('a')).toBe('A');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should handle strings that start with uppercase', () => {
|
|
22
|
+
expect(capitalise('HELLO')).toBe('HELLO');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should handle empty strings', () => {
|
|
26
|
+
expect(capitalise('')).toBe('');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle strings with numbers at the start', () => {
|
|
30
|
+
expect(capitalise('123abc')).toBe('123abc');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should handle strings with special characters at the start', () => {
|
|
34
|
+
expect(capitalise('!hello')).toBe('!hello');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should preserve the rest of the string as-is', () => {
|
|
38
|
+
expect(capitalise('hELLO')).toBe('HELLO');
|
|
39
|
+
});
|
|
40
|
+
});
|