@dnanpm/styleguide 3.11.3 → 3.11.5

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.
Files changed (25) hide show
  1. package/build/cjs/components/Button/Button.d.ts +8 -1
  2. package/build/cjs/components/Button/Button.js +2 -1
  3. package/build/cjs/components/DateTimePicker/DateTimePicker.js +41 -6
  4. package/build/cjs/components/Footer/Components/FooterComponents.js +26 -3
  5. package/build/cjs/components/MainHeaderNavigation/ChildComponents/LinkModifier.js +17 -3
  6. package/build/cjs/components/MainHeaderNavigation/MainHeaderNavigation.d.ts +2 -1
  7. package/build/cjs/components/MainHeaderNavigation/MainHeaderNavigation.js +3 -1
  8. package/build/cjs/components/MainHeaderNavigation/context/NavContext.d.ts +1 -0
  9. package/build/cjs/components/MainHeaderNavigation/context/NavContext.js +1 -0
  10. package/build/cjs/components/RadioButton/RadioButton.d.ts +2 -2
  11. package/build/cjs/components/RadioButton/RadioButton.js +8 -2
  12. package/build/cjs/components/Textarea/Textarea.js +5 -4
  13. package/build/es/components/Button/Button.d.ts +8 -1
  14. package/build/es/components/Button/Button.js +2 -1
  15. package/build/es/components/DateTimePicker/DateTimePicker.js +41 -6
  16. package/build/es/components/Footer/Components/FooterComponents.js +26 -3
  17. package/build/es/components/MainHeaderNavigation/ChildComponents/LinkModifier.js +17 -3
  18. package/build/es/components/MainHeaderNavigation/MainHeaderNavigation.d.ts +2 -1
  19. package/build/es/components/MainHeaderNavigation/MainHeaderNavigation.js +3 -1
  20. package/build/es/components/MainHeaderNavigation/context/NavContext.d.ts +1 -0
  21. package/build/es/components/MainHeaderNavigation/context/NavContext.js +1 -0
  22. package/build/es/components/RadioButton/RadioButton.d.ts +2 -2
  23. package/build/es/components/RadioButton/RadioButton.js +8 -2
  24. package/build/es/components/Textarea/Textarea.js +5 -4
  25. package/package.json +5 -6
@@ -1,4 +1,4 @@
1
- import type { MouseEvent, ReactNode } from 'react';
1
+ import type { ComponentType, MouseEvent, ReactNode } from 'react';
2
2
  import React from 'react';
3
3
  type ButtonType = 'submit' | 'button' | 'reset';
4
4
  export interface Props {
@@ -24,6 +24,13 @@ export interface Props {
24
24
  * Allows to change the type of resulting HTML element from button (`<button></button>`) to anchor (`<a href="..."></a>`)
25
25
  */
26
26
  href?: string;
27
+ /**
28
+ * Allows to use a custom link component instead of anchor element when `href` is defined
29
+ */
30
+ nextLink?: ComponentType<{
31
+ href: string;
32
+ children: ReactNode;
33
+ }>;
27
34
  /**
28
35
  * Allows to set the target attribute for the link
29
36
  */
@@ -125,8 +125,9 @@ const Element = styledComponents.styled.button `
125
125
  `;
126
126
  /** @visibleName Button */
127
127
  const Button = (_a) => {
128
+ var _b;
128
129
  var { type = 'submit', 'data-testid': dataTestId, 'data-no-close': dataNoClose, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = tslib.__rest(_a, ["type", 'data-testid', 'data-no-close', 'data-track-value', 'aria-label']);
129
- return (React__default.default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, "$small": props.small, "$darkBg": props.darkBg, "$fullWidth": props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
130
+ return (React__default.default.createElement(Element, Object.assign({ id: props.id, as: (_b = props.nextLink) !== null && _b !== void 0 ? _b : (props.href ? 'a' : undefined), type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, "$small": props.small, "$darkBg": props.darkBg, "$fullWidth": props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
130
131
  name: props.name,
131
132
  disabled: props.disabled,
132
133
  })), props.loading ? (React__default.default.createElement(PixelLoader.default, { color: props.darkBg ? theme.default.color.default.white : theme.default.color.default.plum, label: props.loadingLabel })) : (React__default.default.createElement("span", { "data-testid": dataTestId && `${dataTestId}-text`, "data-no-close": dataNoClose }, props.children))));
@@ -56,6 +56,15 @@ const localeData = {
56
56
  'en-GB': undefined,
57
57
  };
58
58
  const sentenceCase = (string) => `${string[0].toUpperCase()}${string.slice(1)}`;
59
+ const parseFIDateString = (dateString) => {
60
+ const parts = dateString.split(/\D+/);
61
+ if (!parts || parts.length < 3) {
62
+ return null;
63
+ }
64
+ const [day, month, year] = parts.map(Number);
65
+ const date = new Date(year, month - 1, day);
66
+ return !Number.isNaN(date.getTime()) ? date : null;
67
+ };
59
68
  // TODO: change `& .react-datepicker` value back to `1` after z-index of Footer removed
60
69
  const Wrapper = styledComponents.styled.div `
61
70
  position: relative;
@@ -111,10 +120,27 @@ const Wrapper = styledComponents.styled.div `
111
120
  }
112
121
  }
113
122
 
123
+ & .react-datepicker__sr-only {
124
+ position: absolute;
125
+ width: 1px;
126
+ height: 1px;
127
+ padding: 0;
128
+ overflow: hidden;
129
+ clip: rect(0, 0, 0, 0);
130
+ white-space: nowrap;
131
+ border: 0;
132
+ }
133
+
134
+ & .react-datepicker__day-names {
135
+ background-color: ${theme.default.color.background.sand.E01};
136
+ padding: ${styledUtils.getMultipliedSize(theme.default.base.baseWidth, 0.5)} 0;
137
+ }
138
+
114
139
  & .react-datepicker__day-names,
115
140
  & .react-datepicker__week {
116
141
  display: flex;
117
142
  gap: ${styledUtils.getMultipliedSize(theme.default.base.baseWidth, 0.8)};
143
+ justify-content: center;
118
144
 
119
145
  .react-datepicker__day-name,
120
146
  .react-datepicker__day {
@@ -250,6 +276,7 @@ const DateTimePicker = (_a) => {
250
276
  var _b, _c, _d;
251
277
  var { date: startDate, locale = 'fi-FI', isDatePicker = true, isTimePicker = false, timeInterval = 30, 'data-testid': dataTestId } = _a, props = tslib.__rest(_a, ["date", "locale", "isDatePicker", "isTimePicker", "timeInterval", 'data-testid']);
252
278
  const [showReactDatePicker, setShowReactDatePicker] = React.useState(false);
279
+ const [manualInputValue, setManualInputValue] = React.useState(null);
253
280
  const isRange = props.endDate !== undefined;
254
281
  const callOnChange = (date, event) => {
255
282
  props.onChange(date, event);
@@ -258,6 +285,10 @@ const DateTimePicker = (_a) => {
258
285
  setShowReactDatePicker(true);
259
286
  };
260
287
  const handleOnInputChange = (_, e) => {
288
+ if (props.isEditable) {
289
+ setManualInputValue(e.target.value);
290
+ return;
291
+ }
261
292
  if (props.onInputChange) {
262
293
  props.onInputChange(e);
263
294
  }
@@ -297,6 +328,7 @@ const DateTimePicker = (_a) => {
297
328
  if (!shouldKeepOpen) {
298
329
  setShowReactDatePicker(false);
299
330
  }
331
+ setManualInputValue(null);
300
332
  callOnChange(date, event);
301
333
  };
302
334
  const handleOnReactDatePickerClickOutside = () => {
@@ -313,12 +345,15 @@ const DateTimePicker = (_a) => {
313
345
  .join(' - ');
314
346
  const getDateTimePickerCustomHeader = (args) => DateTimePickerCustomHeader(Object.assign(Object.assign({}, args), { locale, ariaLabelPreviousMonth: props.ariaLabelPreviousMonth, ariaLabelNextMonth: props.ariaLabelNextMonth }));
315
347
  return (React__default.default.createElement(Wrapper, { id: props.id, className: props.className, "data-testid": dataTestId },
316
- React__default.default.createElement(Input.default, { id: `datetimepicker-input-${props.id}`, name: `datetimepicker-input-${props.id}`, label: props.label, placeholder: props.placeholder, value: formatInputValue, onFocus: handleOnInputFocus, onChange: handleOnInputChange, onKeyDown: handleOnKeyDown, onClearableButtonClick: props.isClearable ? handleClearable : undefined, className: "react-datepicker-ignore-onclickoutside", "data-testid": dataTestId && `${dataTestId}-datetimepicker-input`, disabled: props.isDisabled, required: props.isRequired, readonlyUnstyled: !props.isEditable, status: props.isInError ? 'error' : undefined, errorMessage: props.errorMessage, ariaLabel: props.ariaLabelInput, onBlur: (_val, e) => {
317
- if (props.isEditable && e.target.value) {
318
- const parsedDate = new Date(e.target.value);
319
- if (!Number.isNaN(parsedDate.getTime())) {
320
- callOnChange(parsedDate, e);
321
- }
348
+ React__default.default.createElement(Input.default, { id: `datetimepicker-input-${props.id}`, name: `datetimepicker-input-${props.id}`, label: props.label, placeholder: props.placeholder, value: manualInputValue !== null && manualInputValue !== void 0 ? manualInputValue : formatInputValue, onFocus: handleOnInputFocus, onChange: handleOnInputChange, onKeyDown: handleOnKeyDown, onClearableButtonClick: props.isClearable ? handleClearable : undefined, className: "react-datepicker-ignore-onclickoutside", "data-testid": dataTestId && `${dataTestId}-datetimepicker-input`, disabled: props.isDisabled, required: props.isRequired, readonlyUnstyled: !props.isEditable, status: props.isInError ? 'error' : undefined, errorMessage: props.errorMessage, ariaLabel: props.ariaLabelInput, onBlur: (_val, e) => {
349
+ if (!props.isEditable || !e.target.value) {
350
+ return;
351
+ }
352
+ const date = locale === 'fi-FI'
353
+ ? parseFIDateString(e.target.value)
354
+ : new Date(e.target.value);
355
+ if (date && !Number.isNaN(date.getTime())) {
356
+ callOnChange(date, e);
322
357
  }
323
358
  } }),
324
359
  React__default.default.createElement(VisuallyHiddenStatus, { role: "status", "aria-live": "polite" }, startDate instanceof Date && !Number.isNaN(startDate.getTime())
@@ -283,14 +283,37 @@ const Categories = ({ categories }) => {
283
283
  const GeneralInformation = ({ generalInformation }) => {
284
284
  const { language } = React.useContext(FooterContext.default);
285
285
  const currentYear = new Date().getFullYear();
286
- const orderMap = ['privacyPolicy', 'cookieSettings', 'terms'];
286
+ const COOKIE_SETTINGS_URL = 'cookie-settings';
287
+ const OT_SDK_CLASSNAME = 'ot-sdk-show-settings';
288
+ // TODO after release: Remove redundant `isNewModelUsed` condition and refactor it's usages (STYLE-905)
289
+ const isNewModelUsed = !!generalInformation.mandatoryLinks;
287
290
  const typeToClassMap = {
288
291
  cookie: 'ot-sdk-show-settings',
289
292
  };
290
- const orderedGeneralInformation = orderMap.map(id => new Map(Object.entries(generalInformation)).get(id));
293
+ const orderMap = ['privacyPolicy', 'cookieSettings', 'terms'];
294
+ const getOrderedLinks = () => {
295
+ if (isNewModelUsed && Array.isArray(generalInformation.mandatoryLinks)) {
296
+ return generalInformation.mandatoryLinks;
297
+ }
298
+ return orderMap.map(id => new Map(Object.entries(generalInformation)).get(id));
299
+ };
300
+ const getLinkHref = (item) => {
301
+ var _a;
302
+ if (!((_a = item.urls) === null || _a === void 0 ? void 0 : _a[language]))
303
+ return '#!';
304
+ return item.urls[language] !== COOKIE_SETTINGS_URL ? item.urls[language] : '#!';
305
+ };
306
+ const getLinkClassName = (item) => {
307
+ var _a;
308
+ if (isNewModelUsed) {
309
+ return ((_a = item.urls) === null || _a === void 0 ? void 0 : _a[language]) === COOKIE_SETTINGS_URL ? OT_SDK_CLASSNAME : '';
310
+ }
311
+ return item.type ? typeToClassMap[item.type] : '';
312
+ };
313
+ const orderedGeneralInformation = getOrderedLinks();
291
314
  return (React__default.default.createElement(GeneralInformationContainer, { "data-testid": "general-information-section" },
292
315
  orderedGeneralInformation.map((item, index) => item && (React__default.default.createElement(React.Fragment, { key: `general-information-link-${index}` },
293
- React__default.default.createElement("a", { href: (item.urls && item.urls[language]) || '#!', className: item.type && typeToClassMap[item.type] }, item.titles[language]),
316
+ React__default.default.createElement("a", { href: getLinkHref(item), className: getLinkClassName(item) }, item.titles[language]),
294
317
  React__default.default.createElement("span", { "aria-hidden": "true" }, "|")))),
295
318
  "DNA ",
296
319
  currentYear));
@@ -11,6 +11,7 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
11
11
 
12
12
  var React__default = /*#__PURE__*/_interopDefaultCompat(React);
13
13
 
14
+ /* eslint-disable react/jsx-props-no-spreading */
14
15
  const isSelected = (currentUrl, linkUrl, exactMatch = false) => {
15
16
  if (currentUrl && linkUrl) {
16
17
  let url = linkUrl;
@@ -23,15 +24,28 @@ const isSelected = (currentUrl, linkUrl, exactMatch = false) => {
23
24
  };
24
25
  const isMenuItemLinkAbsolute = (menuItemLink) => menuItemLink.includes('http');
25
26
  const LinkModifier = ({ menuItem, disabledTabIndex, showIcon }) => {
26
- const { lang, currentUrl, nextJSLinkComponent, resetMenuEvents } = React.useContext(NavContext.default);
27
+ const { lang, currentUrl, nextJSLinkComponent, nextJSLinkLegacyBehavior, resetMenuEvents } = React.useContext(NavContext.default);
27
28
  const menuItemLink = menuItem.urls[lang];
28
29
  const isExternalLink = menuItem.target === '_blank';
29
- const MenuLinkComponent = (React__default.default.createElement(globalNavStyles.MenuLink, { "aria-current": isSelected(currentUrl, menuItemLink) ? 'page' : undefined, href: menuItemLink, id: menuItem.id, onClick: resetMenuEvents, tabIndex: disabledTabIndex ? -1 : undefined, target: isExternalLink ? '_blank' : '_self' },
30
+ const linkContent = (React__default.default.createElement(React__default.default.Fragment, null,
30
31
  showIcon && (isExternalLink ? React__default.default.createElement(icons.Open, null) : React__default.default.createElement(icons.ArrowRight, null)),
31
32
  React__default.default.createElement("span", null, menuItem.titles[lang])));
33
+ const MenuLinkComponent = (React__default.default.createElement(globalNavStyles.MenuLink, { "aria-current": isSelected(currentUrl, menuItemLink) ? 'page' : undefined, href: menuItemLink, id: menuItem.id, onClick: resetMenuEvents, tabIndex: disabledTabIndex ? -1 : undefined, target: isExternalLink ? '_blank' : '_self' }, linkContent));
34
+ const commonLinkProps = {
35
+ 'aria-current': isSelected(currentUrl, menuItemLink) ? 'page' : undefined,
36
+ linkUrl: menuItemLink,
37
+ id: menuItem.id,
38
+ onClick: resetMenuEvents,
39
+ tabIndex: disabledTabIndex ? -1 : undefined,
40
+ target: isExternalLink ? '_blank' : '_self',
41
+ };
32
42
  if (nextJSLinkComponent && !isMenuItemLinkAbsolute(menuItemLink)) {
33
43
  const NextJSLinkComponent = nextJSLinkComponent;
34
- return (React__default.default.createElement(NextJSLinkComponent, { linkUrl: menuItemLink }, MenuLinkComponent));
44
+ if (nextJSLinkLegacyBehavior) {
45
+ return (React__default.default.createElement(NextJSLinkComponent, { linkUrl: menuItemLink },
46
+ React__default.default.createElement(globalNavStyles.MenuLink, Object.assign({}, commonLinkProps), linkContent)));
47
+ }
48
+ return (React__default.default.createElement(globalNavStyles.MenuLink, Object.assign({ as: NextJSLinkComponent }, commonLinkProps), linkContent));
35
49
  }
36
50
  return MenuLinkComponent;
37
51
  };
@@ -42,10 +42,11 @@ export interface MainHeaderNavigationProps {
42
42
  isLoggedIn?: boolean;
43
43
  collapseSize?: number;
44
44
  nextJSLinkComponent: ComponentType | ReactNode | boolean;
45
+ nextJSLinkLegacyBehavior?: boolean;
45
46
  currentUrl?: string;
46
47
  notificationText?: string;
47
48
  }
48
49
  /** @visibleName MainHeaderNavigation */
49
- declare const MainHeaderNavigation: ({ backToPreviousCategoryLabel, businessMenuAriaLabel, categoryCollectionText, closeMainMenuAriaLabel, className, collapseSize, currentUrl, dnaLogoLinkAriaLabel, featuredItemsAriaLabel, isLoggedIn, items, language, languageSelector, languageSelectorText, login, loginComponent, loginText, mainMenuAriaLabel, minicart, minicartAmount, minicartAmountLabel, minicartComponent, minicartText, nextJSLinkComponent, notificationText, openMainMenuAriaLabel, search, searchComponent, searchText, showLoginTooltip, showMinicart, zIndex, ...props }: MainHeaderNavigationProps) => React.JSX.Element;
50
+ declare const MainHeaderNavigation: ({ backToPreviousCategoryLabel, businessMenuAriaLabel, categoryCollectionText, closeMainMenuAriaLabel, className, collapseSize, currentUrl, dnaLogoLinkAriaLabel, featuredItemsAriaLabel, isLoggedIn, items, language, languageSelector, languageSelectorText, login, loginComponent, loginText, mainMenuAriaLabel, minicart, minicartAmount, minicartAmountLabel, minicartComponent, minicartText, nextJSLinkComponent, nextJSLinkLegacyBehavior, notificationText, openMainMenuAriaLabel, search, searchComponent, searchText, showLoginTooltip, showMinicart, zIndex, ...props }: MainHeaderNavigationProps) => React.JSX.Element;
50
51
  /** @component */
51
52
  export default MainHeaderNavigation;
@@ -134,7 +134,7 @@ const initiateScrollToHeader = (componentRef) => {
134
134
  /** @visibleName MainHeaderNavigation */
135
135
  const MainHeaderNavigation = (_a) => {
136
136
  var _b, _c, _d;
137
- var { backToPreviousCategoryLabel = '', businessMenuAriaLabel = 'Sivustot', categoryCollectionText = 'Muut kategoriat', closeMainMenuAriaLabel, className, collapseSize = 767, currentUrl = '', dnaLogoLinkAriaLabel, featuredItemsAriaLabel = '', isLoggedIn = false, items = {}, language = 'fi', languageSelector = false, languageSelectorText = 'Vaihda kieltä', login = true, loginComponent, loginText = 'Kirjaudu', mainMenuAriaLabel = 'Päänavigaatio', minicart = true, minicartAmount = 0, minicartAmountLabel, minicartComponent = false, minicartText = 'Ostoskori', nextJSLinkComponent = false, notificationText = '', openMainMenuAriaLabel, search = true, searchComponent = false, searchText = 'Haku', showLoginTooltip = false, showMinicart = false, zIndex = 1030 } = _a, props = tslib.__rest(_a, ["backToPreviousCategoryLabel", "businessMenuAriaLabel", "categoryCollectionText", "closeMainMenuAriaLabel", "className", "collapseSize", "currentUrl", "dnaLogoLinkAriaLabel", "featuredItemsAriaLabel", "isLoggedIn", "items", "language", "languageSelector", "languageSelectorText", "login", "loginComponent", "loginText", "mainMenuAriaLabel", "minicart", "minicartAmount", "minicartAmountLabel", "minicartComponent", "minicartText", "nextJSLinkComponent", "notificationText", "openMainMenuAriaLabel", "search", "searchComponent", "searchText", "showLoginTooltip", "showMinicart", "zIndex"]);
137
+ var { backToPreviousCategoryLabel = '', businessMenuAriaLabel = 'Sivustot', categoryCollectionText = 'Muut kategoriat', closeMainMenuAriaLabel, className, collapseSize = 767, currentUrl = '', dnaLogoLinkAriaLabel, featuredItemsAriaLabel = '', isLoggedIn = false, items = {}, language = 'fi', languageSelector = false, languageSelectorText = 'Vaihda kieltä', login = true, loginComponent, loginText = 'Kirjaudu', mainMenuAriaLabel = 'Päänavigaatio', minicart = true, minicartAmount = 0, minicartAmountLabel, minicartComponent = false, minicartText = 'Ostoskori', nextJSLinkComponent = false, nextJSLinkLegacyBehavior = true, notificationText = '', openMainMenuAriaLabel, search = true, searchComponent = false, searchText = 'Haku', showLoginTooltip = false, showMinicart = false, zIndex = 1030 } = _a, props = tslib.__rest(_a, ["backToPreviousCategoryLabel", "businessMenuAriaLabel", "categoryCollectionText", "closeMainMenuAriaLabel", "className", "collapseSize", "currentUrl", "dnaLogoLinkAriaLabel", "featuredItemsAriaLabel", "isLoggedIn", "items", "language", "languageSelector", "languageSelectorText", "login", "loginComponent", "loginText", "mainMenuAriaLabel", "minicart", "minicartAmount", "minicartAmountLabel", "minicartComponent", "minicartText", "nextJSLinkComponent", "nextJSLinkLegacyBehavior", "notificationText", "openMainMenuAriaLabel", "search", "searchComponent", "searchText", "showLoginTooltip", "showMinicart", "zIndex"]);
138
138
  const { isMobile } = useWindowSize.default(collapseSize);
139
139
  const navigationEl = React.useRef(null);
140
140
  const key = ramda.prop('id');
@@ -257,6 +257,7 @@ const MainHeaderNavigation = (_a) => {
257
257
  minicartComponent,
258
258
  navZIndex: zIndex,
259
259
  nextJSLinkComponent,
260
+ nextJSLinkLegacyBehavior,
260
261
  resetMenuEvents,
261
262
  searchComponent,
262
263
  setTooltipItems,
@@ -281,6 +282,7 @@ const MainHeaderNavigation = (_a) => {
281
282
  menuLevel,
282
283
  minicartComponent,
283
284
  nextJSLinkComponent,
285
+ nextJSLinkLegacyBehavior,
284
286
  searchComponent,
285
287
  showLoginTooltip,
286
288
  showMinicart,
@@ -20,6 +20,7 @@ export interface NavContextProps {
20
20
  minicartComponent?: ComponentType | ReactNode | boolean;
21
21
  navZIndex: number;
22
22
  nextJSLinkComponent: ComponentType | ReactNode | boolean;
23
+ nextJSLinkLegacyBehavior?: boolean;
23
24
  resetMenuEvents: () => void;
24
25
  searchComponent?: ComponentType | ReactNode | boolean;
25
26
  setTooltipItems: Dispatch<SetStateAction<MenuTooltipItems>>;
@@ -43,6 +43,7 @@ const NavContext = React.createContext({
43
43
  minicartComponent: undefined,
44
44
  navZIndex: 1030,
45
45
  nextJSLinkComponent: false,
46
+ nextJSLinkLegacyBehavior: true,
46
47
  resetMenuEvents: () => { },
47
48
  searchComponent: false,
48
49
  setTooltipItems: () => { },
@@ -46,7 +46,7 @@ interface RadioButtonProps {
46
46
  }
47
47
  type RadioGroupOption = Omit<RadioButtonProps, 'checked' | 'onChange' | 'name'>;
48
48
  type GroupVariant = 'fieldset' | 'radiogroup';
49
- interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked' | 'disabled'> {
49
+ interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked' | 'disabled' | 'label'> {
50
50
  /**
51
51
  * Unique ID for the radio button group
52
52
  */
@@ -64,7 +64,7 @@ interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked'
64
64
  * Label for the group
65
65
  * Will be rendered inside a `<legend>` or a `<div>`, depending on `groupVariant`
66
66
  */
67
- label?: string;
67
+ label?: string | React.ReactNode;
68
68
  /**
69
69
  * Helper text shown below the group label
70
70
  */
@@ -98,9 +98,15 @@ const StyledFieldset = styledComponents.styled.fieldset `
98
98
  min-inline-size: unset;
99
99
  `;
100
100
  const RadioButtonGroup = ({ label, description, groupName, value, onChange, id, options, children, groupVariant, className, 'data-testid': dataTestId, }) => {
101
- var _a;
102
101
  const isFieldset = groupVariant === 'fieldset';
103
- const baseId = (_a = id !== null && id !== void 0 ? id : label === null || label === void 0 ? void 0 : label.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, '-').replace(/[^a-zA-Z0-9-_]/g, '').toLowerCase()) !== null && _a !== void 0 ? _a : 'radio-group';
102
+ const baseId = id !== null && id !== void 0 ? id : (typeof label === 'string'
103
+ ? label
104
+ .normalize('NFKD')
105
+ .replace(/[\u0300-\u036f]/g, '')
106
+ .replace(/\s+/g, '-')
107
+ .replace(/[^a-zA-Z0-9-_]/g, '')
108
+ .toLowerCase()
109
+ : 'radio-group');
104
110
  const labelId = `${baseId}-label`;
105
111
  const descriptionId = `${baseId}-description`;
106
112
  const combinedDescribedBy = [description ? descriptionId : undefined].filter(Boolean).join(' ');
@@ -64,6 +64,7 @@ const ErrorMessage = styledComponents.styled(Message) `
64
64
  const Textarea = (_a) => {
65
65
  var { height = 3, 'data-testid': dataTestId, ariaLabel } = _a, props = tslib.__rest(_a, ["height", 'data-testid', "ariaLabel"]);
66
66
  const inputRef = React.useRef(null);
67
+ const isErrorStatus = props.status === 'error';
67
68
  const handleOnBlur = (e) => {
68
69
  if (props.onBlur) {
69
70
  props.onBlur(e.target.value, e);
@@ -80,14 +81,14 @@ const Textarea = (_a) => {
80
81
  inputRef.current.focus();
81
82
  }
82
83
  };
83
- const errorId = props.status === 'error' && props.errorMessage ? `${props.id}-error` : undefined;
84
+ const errorId = isErrorStatus && props.errorMessage ? `${props.id}-error` : undefined;
84
85
  const commentId = props.commentMessage ? `${props.id}-comment` : undefined;
85
86
  const describedBy = [errorId, commentId].filter(Boolean).join(' ') || undefined;
86
87
  return (React__default.default.createElement(FieldContainer, { className: props.className },
87
- props.label && (React__default.default.createElement(LabelText.default, { htmlFor: props.id, "data-testid": dataTestId && `${dataTestId}-label`, status: props.status === 'error' ? props.status : undefined, isMandatory: props.required }, props.label)),
88
- React__default.default.createElement(StyledTextarea, { id: props.id, name: props.name, ref: inputRef, value: props.value, rows: height, placeholder: props.placeholder, tabIndex: props.tabIndex, onChange: handleChange, onBlur: handleOnBlur, onFocus: props.onFocus, onClick: onClick, onKeyDown: props.onKeyDown, onKeyPress: props.onKeyPress, required: props.required, disabled: props.disabled, "aria-label": !props.label ? ariaLabel : undefined, "aria-describedby": describedBy, "data-testid": dataTestId }),
88
+ props.label && (React__default.default.createElement(LabelText.default, { htmlFor: props.id, "data-testid": dataTestId && `${dataTestId}-label`, status: isErrorStatus ? 'error' : undefined, isMandatory: props.required }, props.label)),
89
+ React__default.default.createElement(StyledTextarea, { id: props.id, name: props.name, ref: inputRef, value: props.value, rows: height, placeholder: props.placeholder, tabIndex: props.tabIndex, onChange: handleChange, onBlur: handleOnBlur, onFocus: props.onFocus, onClick: onClick, onKeyDown: props.onKeyDown, onKeyPress: props.onKeyPress, required: props.required, disabled: props.disabled, "aria-label": !props.label ? ariaLabel : undefined, "aria-invalid": isErrorStatus, "aria-describedby": describedBy, "data-testid": dataTestId }),
89
90
  props.commentMessage && (React__default.default.createElement(Message, { id: commentId, "data-testid": dataTestId && `${dataTestId}-comment` }, props.commentMessage)),
90
- props.status === 'error' && props.errorMessage && (React__default.default.createElement(ErrorMessage, { id: errorId, "data-testid": dataTestId && `${dataTestId}-error` }, props.errorMessage))));
91
+ isErrorStatus && props.errorMessage && (React__default.default.createElement(ErrorMessage, { id: errorId, "data-testid": dataTestId && `${dataTestId}-error` }, props.errorMessage))));
91
92
  };
92
93
 
93
94
  exports.default = Textarea;
@@ -1,4 +1,4 @@
1
- import type { MouseEvent, ReactNode } from 'react';
1
+ import type { ComponentType, MouseEvent, ReactNode } from 'react';
2
2
  import React from 'react';
3
3
  type ButtonType = 'submit' | 'button' | 'reset';
4
4
  export interface Props {
@@ -24,6 +24,13 @@ export interface Props {
24
24
  * Allows to change the type of resulting HTML element from button (`<button></button>`) to anchor (`<a href="..."></a>`)
25
25
  */
26
26
  href?: string;
27
+ /**
28
+ * Allows to use a custom link component instead of anchor element when `href` is defined
29
+ */
30
+ nextLink?: ComponentType<{
31
+ href: string;
32
+ children: ReactNode;
33
+ }>;
27
34
  /**
28
35
  * Allows to set the target attribute for the link
29
36
  */
@@ -117,8 +117,9 @@ const Element = styled.button `
117
117
  `;
118
118
  /** @visibleName Button */
119
119
  const Button = (_a) => {
120
+ var _b;
120
121
  var { type = 'submit', 'data-testid': dataTestId, 'data-no-close': dataNoClose, 'data-track-value': dataTrackValue, 'aria-label': ariaLabel } = _a, props = __rest(_a, ["type", 'data-testid', 'data-no-close', 'data-track-value', 'aria-label']);
121
- return (React__default.createElement(Element, Object.assign({ id: props.id, as: props.href ? 'a' : undefined, type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, "$small": props.small, "$darkBg": props.darkBg, "$fullWidth": props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
122
+ return (React__default.createElement(Element, Object.assign({ id: props.id, as: (_b = props.nextLink) !== null && _b !== void 0 ? _b : (props.href ? 'a' : undefined), type: props.href ? undefined : type, href: props.href, target: props.href ? props.target : undefined, rel: props.target === '_blank' ? 'noopener noreferrer' : undefined, onClick: props.onClick, onMouseDown: props.onMouseDown, "$small": props.small, "$darkBg": props.darkBg, "$fullWidth": props.fullWidth, "$loading": props.loading, tabIndex: props.loading ? -1 : 0, "data-loading": props.loading, className: props.className, "data-testid": dataTestId, "data-no-close": dataNoClose, "data-track-value": dataTrackValue, "aria-label": ariaLabel }, props.dataAttributes, (!props.href && {
122
123
  name: props.name,
123
124
  disabled: props.disabled,
124
125
  })), props.loading ? (React__default.createElement(PixelLoader, { color: props.darkBg ? theme.color.default.white : theme.color.default.plum, label: props.loadingLabel })) : (React__default.createElement("span", { "data-testid": dataTestId && `${dataTestId}-text`, "data-no-close": dataNoClose }, props.children))));
@@ -47,6 +47,15 @@ const localeData = {
47
47
  'en-GB': undefined,
48
48
  };
49
49
  const sentenceCase = (string) => `${string[0].toUpperCase()}${string.slice(1)}`;
50
+ const parseFIDateString = (dateString) => {
51
+ const parts = dateString.split(/\D+/);
52
+ if (!parts || parts.length < 3) {
53
+ return null;
54
+ }
55
+ const [day, month, year] = parts.map(Number);
56
+ const date = new Date(year, month - 1, day);
57
+ return !Number.isNaN(date.getTime()) ? date : null;
58
+ };
50
59
  // TODO: change `& .react-datepicker` value back to `1` after z-index of Footer removed
51
60
  const Wrapper = styled.div `
52
61
  position: relative;
@@ -102,10 +111,27 @@ const Wrapper = styled.div `
102
111
  }
103
112
  }
104
113
 
114
+ & .react-datepicker__sr-only {
115
+ position: absolute;
116
+ width: 1px;
117
+ height: 1px;
118
+ padding: 0;
119
+ overflow: hidden;
120
+ clip: rect(0, 0, 0, 0);
121
+ white-space: nowrap;
122
+ border: 0;
123
+ }
124
+
125
+ & .react-datepicker__day-names {
126
+ background-color: ${theme.color.background.sand.E01};
127
+ padding: ${getMultipliedSize(theme.base.baseWidth, 0.5)} 0;
128
+ }
129
+
105
130
  & .react-datepicker__day-names,
106
131
  & .react-datepicker__week {
107
132
  display: flex;
108
133
  gap: ${getMultipliedSize(theme.base.baseWidth, 0.8)};
134
+ justify-content: center;
109
135
 
110
136
  .react-datepicker__day-name,
111
137
  .react-datepicker__day {
@@ -241,6 +267,7 @@ const DateTimePicker = (_a) => {
241
267
  var _b, _c, _d;
242
268
  var { date: startDate, locale = 'fi-FI', isDatePicker = true, isTimePicker = false, timeInterval = 30, 'data-testid': dataTestId } = _a, props = __rest(_a, ["date", "locale", "isDatePicker", "isTimePicker", "timeInterval", 'data-testid']);
243
269
  const [showReactDatePicker, setShowReactDatePicker] = useState(false);
270
+ const [manualInputValue, setManualInputValue] = useState(null);
244
271
  const isRange = props.endDate !== undefined;
245
272
  const callOnChange = (date, event) => {
246
273
  props.onChange(date, event);
@@ -249,6 +276,10 @@ const DateTimePicker = (_a) => {
249
276
  setShowReactDatePicker(true);
250
277
  };
251
278
  const handleOnInputChange = (_, e) => {
279
+ if (props.isEditable) {
280
+ setManualInputValue(e.target.value);
281
+ return;
282
+ }
252
283
  if (props.onInputChange) {
253
284
  props.onInputChange(e);
254
285
  }
@@ -288,6 +319,7 @@ const DateTimePicker = (_a) => {
288
319
  if (!shouldKeepOpen) {
289
320
  setShowReactDatePicker(false);
290
321
  }
322
+ setManualInputValue(null);
291
323
  callOnChange(date, event);
292
324
  };
293
325
  const handleOnReactDatePickerClickOutside = () => {
@@ -304,12 +336,15 @@ const DateTimePicker = (_a) => {
304
336
  .join(' - ');
305
337
  const getDateTimePickerCustomHeader = (args) => DateTimePickerCustomHeader(Object.assign(Object.assign({}, args), { locale, ariaLabelPreviousMonth: props.ariaLabelPreviousMonth, ariaLabelNextMonth: props.ariaLabelNextMonth }));
306
338
  return (React__default.createElement(Wrapper, { id: props.id, className: props.className, "data-testid": dataTestId },
307
- React__default.createElement(Input, { id: `datetimepicker-input-${props.id}`, name: `datetimepicker-input-${props.id}`, label: props.label, placeholder: props.placeholder, value: formatInputValue, onFocus: handleOnInputFocus, onChange: handleOnInputChange, onKeyDown: handleOnKeyDown, onClearableButtonClick: props.isClearable ? handleClearable : undefined, className: "react-datepicker-ignore-onclickoutside", "data-testid": dataTestId && `${dataTestId}-datetimepicker-input`, disabled: props.isDisabled, required: props.isRequired, readonlyUnstyled: !props.isEditable, status: props.isInError ? 'error' : undefined, errorMessage: props.errorMessage, ariaLabel: props.ariaLabelInput, onBlur: (_val, e) => {
308
- if (props.isEditable && e.target.value) {
309
- const parsedDate = new Date(e.target.value);
310
- if (!Number.isNaN(parsedDate.getTime())) {
311
- callOnChange(parsedDate, e);
312
- }
339
+ React__default.createElement(Input, { id: `datetimepicker-input-${props.id}`, name: `datetimepicker-input-${props.id}`, label: props.label, placeholder: props.placeholder, value: manualInputValue !== null && manualInputValue !== void 0 ? manualInputValue : formatInputValue, onFocus: handleOnInputFocus, onChange: handleOnInputChange, onKeyDown: handleOnKeyDown, onClearableButtonClick: props.isClearable ? handleClearable : undefined, className: "react-datepicker-ignore-onclickoutside", "data-testid": dataTestId && `${dataTestId}-datetimepicker-input`, disabled: props.isDisabled, required: props.isRequired, readonlyUnstyled: !props.isEditable, status: props.isInError ? 'error' : undefined, errorMessage: props.errorMessage, ariaLabel: props.ariaLabelInput, onBlur: (_val, e) => {
340
+ if (!props.isEditable || !e.target.value) {
341
+ return;
342
+ }
343
+ const date = locale === 'fi-FI'
344
+ ? parseFIDateString(e.target.value)
345
+ : new Date(e.target.value);
346
+ if (date && !Number.isNaN(date.getTime())) {
347
+ callOnChange(date, e);
313
348
  }
314
349
  } }),
315
350
  React__default.createElement(VisuallyHiddenStatus, { role: "status", "aria-live": "polite" }, startDate instanceof Date && !Number.isNaN(startDate.getTime())
@@ -277,14 +277,37 @@ const Categories = ({ categories }) => {
277
277
  const GeneralInformation = ({ generalInformation }) => {
278
278
  const { language } = useContext(FooterContext);
279
279
  const currentYear = new Date().getFullYear();
280
- const orderMap = ['privacyPolicy', 'cookieSettings', 'terms'];
280
+ const COOKIE_SETTINGS_URL = 'cookie-settings';
281
+ const OT_SDK_CLASSNAME = 'ot-sdk-show-settings';
282
+ // TODO after release: Remove redundant `isNewModelUsed` condition and refactor it's usages (STYLE-905)
283
+ const isNewModelUsed = !!generalInformation.mandatoryLinks;
281
284
  const typeToClassMap = {
282
285
  cookie: 'ot-sdk-show-settings',
283
286
  };
284
- const orderedGeneralInformation = orderMap.map(id => new Map(Object.entries(generalInformation)).get(id));
287
+ const orderMap = ['privacyPolicy', 'cookieSettings', 'terms'];
288
+ const getOrderedLinks = () => {
289
+ if (isNewModelUsed && Array.isArray(generalInformation.mandatoryLinks)) {
290
+ return generalInformation.mandatoryLinks;
291
+ }
292
+ return orderMap.map(id => new Map(Object.entries(generalInformation)).get(id));
293
+ };
294
+ const getLinkHref = (item) => {
295
+ var _a;
296
+ if (!((_a = item.urls) === null || _a === void 0 ? void 0 : _a[language]))
297
+ return '#!';
298
+ return item.urls[language] !== COOKIE_SETTINGS_URL ? item.urls[language] : '#!';
299
+ };
300
+ const getLinkClassName = (item) => {
301
+ var _a;
302
+ if (isNewModelUsed) {
303
+ return ((_a = item.urls) === null || _a === void 0 ? void 0 : _a[language]) === COOKIE_SETTINGS_URL ? OT_SDK_CLASSNAME : '';
304
+ }
305
+ return item.type ? typeToClassMap[item.type] : '';
306
+ };
307
+ const orderedGeneralInformation = getOrderedLinks();
285
308
  return (React__default.createElement(GeneralInformationContainer, { "data-testid": "general-information-section" },
286
309
  orderedGeneralInformation.map((item, index) => item && (React__default.createElement(Fragment, { key: `general-information-link-${index}` },
287
- React__default.createElement("a", { href: (item.urls && item.urls[language]) || '#!', className: item.type && typeToClassMap[item.type] }, item.titles[language]),
310
+ React__default.createElement("a", { href: getLinkHref(item), className: getLinkClassName(item) }, item.titles[language]),
288
311
  React__default.createElement("span", { "aria-hidden": "true" }, "|")))),
289
312
  "DNA ",
290
313
  currentYear));
@@ -3,6 +3,7 @@ import React__default, { useContext } from 'react';
3
3
  import NavContext from '../context/NavContext.js';
4
4
  import { MenuLink } from '../globalNavStyles.js';
5
5
 
6
+ /* eslint-disable react/jsx-props-no-spreading */
6
7
  const isSelected = (currentUrl, linkUrl, exactMatch = false) => {
7
8
  if (currentUrl && linkUrl) {
8
9
  let url = linkUrl;
@@ -15,15 +16,28 @@ const isSelected = (currentUrl, linkUrl, exactMatch = false) => {
15
16
  };
16
17
  const isMenuItemLinkAbsolute = (menuItemLink) => menuItemLink.includes('http');
17
18
  const LinkModifier = ({ menuItem, disabledTabIndex, showIcon }) => {
18
- const { lang, currentUrl, nextJSLinkComponent, resetMenuEvents } = useContext(NavContext);
19
+ const { lang, currentUrl, nextJSLinkComponent, nextJSLinkLegacyBehavior, resetMenuEvents } = useContext(NavContext);
19
20
  const menuItemLink = menuItem.urls[lang];
20
21
  const isExternalLink = menuItem.target === '_blank';
21
- const MenuLinkComponent = (React__default.createElement(MenuLink, { "aria-current": isSelected(currentUrl, menuItemLink) ? 'page' : undefined, href: menuItemLink, id: menuItem.id, onClick: resetMenuEvents, tabIndex: disabledTabIndex ? -1 : undefined, target: isExternalLink ? '_blank' : '_self' },
22
+ const linkContent = (React__default.createElement(React__default.Fragment, null,
22
23
  showIcon && (isExternalLink ? React__default.createElement(Open, null) : React__default.createElement(ArrowRight, null)),
23
24
  React__default.createElement("span", null, menuItem.titles[lang])));
25
+ const MenuLinkComponent = (React__default.createElement(MenuLink, { "aria-current": isSelected(currentUrl, menuItemLink) ? 'page' : undefined, href: menuItemLink, id: menuItem.id, onClick: resetMenuEvents, tabIndex: disabledTabIndex ? -1 : undefined, target: isExternalLink ? '_blank' : '_self' }, linkContent));
26
+ const commonLinkProps = {
27
+ 'aria-current': isSelected(currentUrl, menuItemLink) ? 'page' : undefined,
28
+ linkUrl: menuItemLink,
29
+ id: menuItem.id,
30
+ onClick: resetMenuEvents,
31
+ tabIndex: disabledTabIndex ? -1 : undefined,
32
+ target: isExternalLink ? '_blank' : '_self',
33
+ };
24
34
  if (nextJSLinkComponent && !isMenuItemLinkAbsolute(menuItemLink)) {
25
35
  const NextJSLinkComponent = nextJSLinkComponent;
26
- return (React__default.createElement(NextJSLinkComponent, { linkUrl: menuItemLink }, MenuLinkComponent));
36
+ if (nextJSLinkLegacyBehavior) {
37
+ return (React__default.createElement(NextJSLinkComponent, { linkUrl: menuItemLink },
38
+ React__default.createElement(MenuLink, Object.assign({}, commonLinkProps), linkContent)));
39
+ }
40
+ return (React__default.createElement(MenuLink, Object.assign({ as: NextJSLinkComponent }, commonLinkProps), linkContent));
27
41
  }
28
42
  return MenuLinkComponent;
29
43
  };
@@ -42,10 +42,11 @@ export interface MainHeaderNavigationProps {
42
42
  isLoggedIn?: boolean;
43
43
  collapseSize?: number;
44
44
  nextJSLinkComponent: ComponentType | ReactNode | boolean;
45
+ nextJSLinkLegacyBehavior?: boolean;
45
46
  currentUrl?: string;
46
47
  notificationText?: string;
47
48
  }
48
49
  /** @visibleName MainHeaderNavigation */
49
- declare const MainHeaderNavigation: ({ backToPreviousCategoryLabel, businessMenuAriaLabel, categoryCollectionText, closeMainMenuAriaLabel, className, collapseSize, currentUrl, dnaLogoLinkAriaLabel, featuredItemsAriaLabel, isLoggedIn, items, language, languageSelector, languageSelectorText, login, loginComponent, loginText, mainMenuAriaLabel, minicart, minicartAmount, minicartAmountLabel, minicartComponent, minicartText, nextJSLinkComponent, notificationText, openMainMenuAriaLabel, search, searchComponent, searchText, showLoginTooltip, showMinicart, zIndex, ...props }: MainHeaderNavigationProps) => React.JSX.Element;
50
+ declare const MainHeaderNavigation: ({ backToPreviousCategoryLabel, businessMenuAriaLabel, categoryCollectionText, closeMainMenuAriaLabel, className, collapseSize, currentUrl, dnaLogoLinkAriaLabel, featuredItemsAriaLabel, isLoggedIn, items, language, languageSelector, languageSelectorText, login, loginComponent, loginText, mainMenuAriaLabel, minicart, minicartAmount, minicartAmountLabel, minicartComponent, minicartText, nextJSLinkComponent, nextJSLinkLegacyBehavior, notificationText, openMainMenuAriaLabel, search, searchComponent, searchText, showLoginTooltip, showMinicart, zIndex, ...props }: MainHeaderNavigationProps) => React.JSX.Element;
50
51
  /** @component */
51
52
  export default MainHeaderNavigation;
@@ -126,7 +126,7 @@ const initiateScrollToHeader = (componentRef) => {
126
126
  /** @visibleName MainHeaderNavigation */
127
127
  const MainHeaderNavigation = (_a) => {
128
128
  var _b, _c, _d;
129
- var { backToPreviousCategoryLabel = '', businessMenuAriaLabel = 'Sivustot', categoryCollectionText = 'Muut kategoriat', closeMainMenuAriaLabel, className, collapseSize = 767, currentUrl = '', dnaLogoLinkAriaLabel, featuredItemsAriaLabel = '', isLoggedIn = false, items = {}, language = 'fi', languageSelector = false, languageSelectorText = 'Vaihda kieltä', login = true, loginComponent, loginText = 'Kirjaudu', mainMenuAriaLabel = 'Päänavigaatio', minicart = true, minicartAmount = 0, minicartAmountLabel, minicartComponent = false, minicartText = 'Ostoskori', nextJSLinkComponent = false, notificationText = '', openMainMenuAriaLabel, search = true, searchComponent = false, searchText = 'Haku', showLoginTooltip = false, showMinicart = false, zIndex = 1030 } = _a, props = __rest(_a, ["backToPreviousCategoryLabel", "businessMenuAriaLabel", "categoryCollectionText", "closeMainMenuAriaLabel", "className", "collapseSize", "currentUrl", "dnaLogoLinkAriaLabel", "featuredItemsAriaLabel", "isLoggedIn", "items", "language", "languageSelector", "languageSelectorText", "login", "loginComponent", "loginText", "mainMenuAriaLabel", "minicart", "minicartAmount", "minicartAmountLabel", "minicartComponent", "minicartText", "nextJSLinkComponent", "notificationText", "openMainMenuAriaLabel", "search", "searchComponent", "searchText", "showLoginTooltip", "showMinicart", "zIndex"]);
129
+ var { backToPreviousCategoryLabel = '', businessMenuAriaLabel = 'Sivustot', categoryCollectionText = 'Muut kategoriat', closeMainMenuAriaLabel, className, collapseSize = 767, currentUrl = '', dnaLogoLinkAriaLabel, featuredItemsAriaLabel = '', isLoggedIn = false, items = {}, language = 'fi', languageSelector = false, languageSelectorText = 'Vaihda kieltä', login = true, loginComponent, loginText = 'Kirjaudu', mainMenuAriaLabel = 'Päänavigaatio', minicart = true, minicartAmount = 0, minicartAmountLabel, minicartComponent = false, minicartText = 'Ostoskori', nextJSLinkComponent = false, nextJSLinkLegacyBehavior = true, notificationText = '', openMainMenuAriaLabel, search = true, searchComponent = false, searchText = 'Haku', showLoginTooltip = false, showMinicart = false, zIndex = 1030 } = _a, props = __rest(_a, ["backToPreviousCategoryLabel", "businessMenuAriaLabel", "categoryCollectionText", "closeMainMenuAriaLabel", "className", "collapseSize", "currentUrl", "dnaLogoLinkAriaLabel", "featuredItemsAriaLabel", "isLoggedIn", "items", "language", "languageSelector", "languageSelectorText", "login", "loginComponent", "loginText", "mainMenuAriaLabel", "minicart", "minicartAmount", "minicartAmountLabel", "minicartComponent", "minicartText", "nextJSLinkComponent", "nextJSLinkLegacyBehavior", "notificationText", "openMainMenuAriaLabel", "search", "searchComponent", "searchText", "showLoginTooltip", "showMinicart", "zIndex"]);
130
130
  const { isMobile } = useWindowSize(collapseSize);
131
131
  const navigationEl = useRef(null);
132
132
  const key = prop('id');
@@ -249,6 +249,7 @@ const MainHeaderNavigation = (_a) => {
249
249
  minicartComponent,
250
250
  navZIndex: zIndex,
251
251
  nextJSLinkComponent,
252
+ nextJSLinkLegacyBehavior,
252
253
  resetMenuEvents,
253
254
  searchComponent,
254
255
  setTooltipItems,
@@ -273,6 +274,7 @@ const MainHeaderNavigation = (_a) => {
273
274
  menuLevel,
274
275
  minicartComponent,
275
276
  nextJSLinkComponent,
277
+ nextJSLinkLegacyBehavior,
276
278
  searchComponent,
277
279
  showLoginTooltip,
278
280
  showMinicart,
@@ -20,6 +20,7 @@ export interface NavContextProps {
20
20
  minicartComponent?: ComponentType | ReactNode | boolean;
21
21
  navZIndex: number;
22
22
  nextJSLinkComponent: ComponentType | ReactNode | boolean;
23
+ nextJSLinkLegacyBehavior?: boolean;
23
24
  resetMenuEvents: () => void;
24
25
  searchComponent?: ComponentType | ReactNode | boolean;
25
26
  setTooltipItems: Dispatch<SetStateAction<MenuTooltipItems>>;
@@ -39,6 +39,7 @@ const NavContext = createContext({
39
39
  minicartComponent: undefined,
40
40
  navZIndex: 1030,
41
41
  nextJSLinkComponent: false,
42
+ nextJSLinkLegacyBehavior: true,
42
43
  resetMenuEvents: () => { },
43
44
  searchComponent: false,
44
45
  setTooltipItems: () => { },
@@ -46,7 +46,7 @@ interface RadioButtonProps {
46
46
  }
47
47
  type RadioGroupOption = Omit<RadioButtonProps, 'checked' | 'onChange' | 'name'>;
48
48
  type GroupVariant = 'fieldset' | 'radiogroup';
49
- interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked' | 'disabled'> {
49
+ interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked' | 'disabled' | 'label'> {
50
50
  /**
51
51
  * Unique ID for the radio button group
52
52
  */
@@ -64,7 +64,7 @@ interface RadioButtonGroupProps extends Omit<RadioButtonProps, 'id' | 'checked'
64
64
  * Label for the group
65
65
  * Will be rendered inside a `<legend>` or a `<div>`, depending on `groupVariant`
66
66
  */
67
- label?: string;
67
+ label?: string | React.ReactNode;
68
68
  /**
69
69
  * Helper text shown below the group label
70
70
  */
@@ -90,9 +90,15 @@ const StyledFieldset = styled.fieldset `
90
90
  min-inline-size: unset;
91
91
  `;
92
92
  const RadioButtonGroup = ({ label, description, groupName, value, onChange, id, options, children, groupVariant, className, 'data-testid': dataTestId, }) => {
93
- var _a;
94
93
  const isFieldset = groupVariant === 'fieldset';
95
- const baseId = (_a = id !== null && id !== void 0 ? id : label === null || label === void 0 ? void 0 : label.normalize('NFKD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, '-').replace(/[^a-zA-Z0-9-_]/g, '').toLowerCase()) !== null && _a !== void 0 ? _a : 'radio-group';
94
+ const baseId = id !== null && id !== void 0 ? id : (typeof label === 'string'
95
+ ? label
96
+ .normalize('NFKD')
97
+ .replace(/[\u0300-\u036f]/g, '')
98
+ .replace(/\s+/g, '-')
99
+ .replace(/[^a-zA-Z0-9-_]/g, '')
100
+ .toLowerCase()
101
+ : 'radio-group');
96
102
  const labelId = `${baseId}-label`;
97
103
  const descriptionId = `${baseId}-description`;
98
104
  const combinedDescribedBy = [description ? descriptionId : undefined].filter(Boolean).join(' ');
@@ -56,6 +56,7 @@ const ErrorMessage = styled(Message) `
56
56
  const Textarea = (_a) => {
57
57
  var { height = 3, 'data-testid': dataTestId, ariaLabel } = _a, props = __rest(_a, ["height", 'data-testid', "ariaLabel"]);
58
58
  const inputRef = useRef(null);
59
+ const isErrorStatus = props.status === 'error';
59
60
  const handleOnBlur = (e) => {
60
61
  if (props.onBlur) {
61
62
  props.onBlur(e.target.value, e);
@@ -72,14 +73,14 @@ const Textarea = (_a) => {
72
73
  inputRef.current.focus();
73
74
  }
74
75
  };
75
- const errorId = props.status === 'error' && props.errorMessage ? `${props.id}-error` : undefined;
76
+ const errorId = isErrorStatus && props.errorMessage ? `${props.id}-error` : undefined;
76
77
  const commentId = props.commentMessage ? `${props.id}-comment` : undefined;
77
78
  const describedBy = [errorId, commentId].filter(Boolean).join(' ') || undefined;
78
79
  return (React__default.createElement(FieldContainer, { className: props.className },
79
- props.label && (React__default.createElement(LabelText, { htmlFor: props.id, "data-testid": dataTestId && `${dataTestId}-label`, status: props.status === 'error' ? props.status : undefined, isMandatory: props.required }, props.label)),
80
- React__default.createElement(StyledTextarea, { id: props.id, name: props.name, ref: inputRef, value: props.value, rows: height, placeholder: props.placeholder, tabIndex: props.tabIndex, onChange: handleChange, onBlur: handleOnBlur, onFocus: props.onFocus, onClick: onClick, onKeyDown: props.onKeyDown, onKeyPress: props.onKeyPress, required: props.required, disabled: props.disabled, "aria-label": !props.label ? ariaLabel : undefined, "aria-describedby": describedBy, "data-testid": dataTestId }),
80
+ props.label && (React__default.createElement(LabelText, { htmlFor: props.id, "data-testid": dataTestId && `${dataTestId}-label`, status: isErrorStatus ? 'error' : undefined, isMandatory: props.required }, props.label)),
81
+ React__default.createElement(StyledTextarea, { id: props.id, name: props.name, ref: inputRef, value: props.value, rows: height, placeholder: props.placeholder, tabIndex: props.tabIndex, onChange: handleChange, onBlur: handleOnBlur, onFocus: props.onFocus, onClick: onClick, onKeyDown: props.onKeyDown, onKeyPress: props.onKeyPress, required: props.required, disabled: props.disabled, "aria-label": !props.label ? ariaLabel : undefined, "aria-invalid": isErrorStatus, "aria-describedby": describedBy, "data-testid": dataTestId }),
81
82
  props.commentMessage && (React__default.createElement(Message, { id: commentId, "data-testid": dataTestId && `${dataTestId}-comment` }, props.commentMessage)),
82
- props.status === 'error' && props.errorMessage && (React__default.createElement(ErrorMessage, { id: errorId, "data-testid": dataTestId && `${dataTestId}-error` }, props.errorMessage))));
83
+ isErrorStatus && props.errorMessage && (React__default.createElement(ErrorMessage, { id: errorId, "data-testid": dataTestId && `${dataTestId}-error` }, props.errorMessage))));
83
84
  };
84
85
 
85
86
  export { Textarea as default };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dnanpm/styleguide",
3
3
  "sideEffects": false,
4
- "version": "v3.11.3",
4
+ "version": "v3.11.5",
5
5
  "main": "build/cjs/index.js",
6
6
  "module": "build/es/index.js",
7
7
  "jsnext:main": "build/es/index.js",
@@ -25,8 +25,8 @@
25
25
  "clean": "rm -rf node_modules build dnanpm-styleguide*.tgz",
26
26
  "eslint": "cross-env eslint --fix './**/*.{ts,tsx}'",
27
27
  "eslint:check": "cross-env eslint './**/*.{ts,tsx}'",
28
- "format": "cross-env prettier --ignore-path .prettierignore --write --loglevel silent '**/*.{ts,tsx,js,json}'",
29
- "format:check": "cross-env prettier --ignore-path .prettierignore --check '**/*.{ts,tsx,js,json}'",
28
+ "format": "cross-env prettier --ignore-path .prettierignore --write --loglevel silent '**/*.{ts,tsx,js,json,md}'",
29
+ "format:check": "cross-env prettier --ignore-path .prettierignore --check '**/*.{ts,tsx,js,json,md}'",
30
30
  "prepublishOnly": "npm run clean && npm ci && npm run eslint:check && npm run format:check && npm run build",
31
31
  "styleguide": "styleguidist server",
32
32
  "styleguide:build": "styleguidist build",
@@ -58,7 +58,6 @@
58
58
  "@types/node": "^17.0.45",
59
59
  "@types/ramda": "^0.27.36",
60
60
  "@types/react": "^18.3.11",
61
- "@types/react-datepicker": "^6.2.0",
62
61
  "@types/react-dom": "^18.3.1",
63
62
  "@types/react-modal": "^3.13.1",
64
63
  "@types/resize-observer-browser": "^0.1.8",
@@ -72,7 +71,7 @@
72
71
  "eslint-config-airbnb-typescript": "^17.1.0",
73
72
  "eslint-config-prettier": "^10.1.8",
74
73
  "eslint-plugin-import": "2.32.0",
75
- "eslint-plugin-jsdoc": "^52.0.2",
74
+ "eslint-plugin-jsdoc": "^56.1.2",
76
75
  "eslint-plugin-jsx-a11y": "^6.10.2",
77
76
  "eslint-plugin-prefer-arrow": "^1.2.3",
78
77
  "eslint-plugin-react": "^7.37.4",
@@ -99,7 +98,7 @@
99
98
  },
100
99
  "dependencies": {
101
100
  "ramda": "^0.27.1",
102
- "react-datepicker": "^8.4.0",
101
+ "react-datepicker": "8.7.0",
103
102
  "react-modal": "^3.16.1",
104
103
  "react-select": "^5.8.1",
105
104
  "react-spring": "^8.0.27",