-
-
-
+
+
+
+ {hasImage && currentVariant === 'imageLeft' && }
+
+ {hasImage && currentVariant === 'imageRight' && }
- )}
+ {!hasImage && !hideKoros && (
+
+ )}
+ {hasImage && (
+
+ )}
+
);
};
diff --git a/packages/react/src/components/highlight/Highlight.tsx b/packages/react/src/components/highlight/Highlight.tsx
index bc976ac6e5..8b8457ee15 100644
--- a/packages/react/src/components/highlight/Highlight.tsx
+++ b/packages/react/src/components/highlight/Highlight.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-// import core base styles
-import 'hds-core';
+import '../../styles/base.module.css';
import classNames from '../../utils/classNames';
import { useTheme } from '../../hooks/useTheme';
import styles from './Highlight.module.scss';
diff --git a/packages/react/src/components/imageWithCard/ImageWithCard.tsx b/packages/react/src/components/imageWithCard/ImageWithCard.tsx
index bd385e4b23..d1ff0b7095 100644
--- a/packages/react/src/components/imageWithCard/ImageWithCard.tsx
+++ b/packages/react/src/components/imageWithCard/ImageWithCard.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import classNames from '../../utils/classNames';
import styles from './ImageWithCard.module.css';
diff --git a/packages/react/src/components/koros/Koros.tsx b/packages/react/src/components/koros/Koros.tsx
index 68c605536c..34ebf96a3d 100644
--- a/packages/react/src/components/koros/Koros.tsx
+++ b/packages/react/src/components/koros/Koros.tsx
@@ -1,9 +1,7 @@
import React, { useState } from 'react';
import uniqueId from 'lodash.uniqueid';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import classNames from '../../utils/classNames';
import styles from './Koros.module.css';
diff --git a/packages/react/src/components/link/Link.tsx b/packages/react/src/components/link/Link.tsx
index 4f119500b1..c9d242c978 100644
--- a/packages/react/src/components/link/Link.tsx
+++ b/packages/react/src/components/link/Link.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Link.module.scss';
import { IconLinkExternal } from '../../icons';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/linkbox/Linkbox.tsx b/packages/react/src/components/linkbox/Linkbox.tsx
index 1e6396ebc3..c974702e33 100644
--- a/packages/react/src/components/linkbox/Linkbox.tsx
+++ b/packages/react/src/components/linkbox/Linkbox.tsx
@@ -1,8 +1,6 @@
import React, { useRef } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Linkbox.module.scss';
import { IconArrowRight, IconLinkExternal } from '../../icons';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/loadingSpinner/LoadingSpinner.tsx b/packages/react/src/components/loadingSpinner/LoadingSpinner.tsx
index 45a7e27cea..3161e56737 100644
--- a/packages/react/src/components/loadingSpinner/LoadingSpinner.tsx
+++ b/packages/react/src/components/loadingSpinner/LoadingSpinner.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './LoadingSpinner.module.scss';
import classNames from '../../utils/classNames';
import { useTheme } from '../../hooks/useTheme';
diff --git a/packages/react/src/components/login/__mocks__/mockWindowLocation.ts b/packages/react/src/components/login/__mocks__/mockWindowLocation.ts
index e2eb826e4d..cbb998e2dc 100644
--- a/packages/react/src/components/login/__mocks__/mockWindowLocation.ts
+++ b/packages/react/src/components/login/__mocks__/mockWindowLocation.ts
@@ -7,20 +7,27 @@ export type MockedWindowLocationActions = {
export default function mockWindowLocation(): MockedWindowLocationActions {
const globalWin = (global as unknown) as Window;
let oldWindowLocation: Location | undefined = globalWin.location;
-
- const unload = () => setTimeout(() => window.dispatchEvent(new Event('unload')), 20);
- const tracker = jest.fn(unload);
+ const tracker = jest.fn();
+ const unload = (...args: unknown[]) => {
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ tracker(...args);
+ setTimeout(() => window.dispatchEvent(new Event('unload')), 20);
+ };
+ // If the tracker is directly set to "value" below,
+ // then calls done with "window.location.assign.bind(window.self)"
+ // will not be tracked. oidc-client-ts does this.
+ const trackerWithUnload = jest.fn(unload);
const location = Object.defineProperties(
{},
{
...Object.getOwnPropertyDescriptors(oldWindowLocation),
assign: {
enumerable: true,
- value: tracker,
+ value: trackerWithUnload,
},
replace: {
enumerable: true,
- value: tracker,
+ value: trackerWithUnload,
},
},
);
@@ -32,6 +39,7 @@ export default function mockWindowLocation(): MockedWindowLocationActions {
});
const getCalls = () => {
+ // console.log('calls', tracker.mock.calls);
return (tracker.mock.calls as unknown) as string[];
};
diff --git a/packages/react/src/components/login/client/oidcClient.test.ts b/packages/react/src/components/login/client/oidcClient.test.ts
index aefbd92883..d529a7b8f0 100644
--- a/packages/react/src/components/login/client/oidcClient.test.ts
+++ b/packages/react/src/components/login/client/oidcClient.test.ts
@@ -80,7 +80,7 @@ describe('oidcClient', () => {
const { userManager } = testData;
const signinRedirect = jest.spyOn(userManager, 'signinRedirect');
const loginParams = { language: 'sv' };
- await waitForLoginToTimeout(loginParams);
+ await waitForLoginToTimeout(mockedWindowControls, loginParams);
expect(signinRedirect).toHaveBeenNthCalledWith(1, {
extraQueryParams: {
ui_locales: loginParams.language,
@@ -103,7 +103,7 @@ describe('oidcClient', () => {
nonce: 'nonce',
state: { stateValue: 1, path: '/applications' },
};
- await waitForLoginToTimeout({ ...loginParams, language: 'sv' });
+ await waitForLoginToTimeout(mockedWindowControls, { ...loginParams, language: 'sv' });
expect(signinRedirect).toHaveBeenNthCalledWith(1, {
...loginParams,
extraQueryParams: {
@@ -123,33 +123,33 @@ describe('oidcClient', () => {
it('should add given language to the logout url', async () => {
const { oidcClient, userManager } = testData;
const signoutRedirectSpy = jest.spyOn(userManager, 'signoutRedirect');
- const loginParams = { language: 'sv' };
- await waitForLogoutToTimeout(loginParams);
+ const logoutParams = { language: 'sv' };
+ await waitForLogoutToTimeout(mockedWindowControls, logoutParams);
expect(signoutRedirectSpy).toHaveBeenCalledTimes(1);
expect(signoutRedirectSpy).toHaveBeenNthCalledWith(1, {
extraQueryParams: {
- ui_locales: loginParams.language,
+ ui_locales: logoutParams.language,
},
});
await waitFor(() => {
- expect(mockedWindowControls.getCallParameters().get('ui_locales')).toBe(loginParams.language);
+ expect(mockedWindowControls.getCallParameters().get('ui_locales')).toBe(logoutParams.language);
});
expect(oidcClient.isAuthenticated()).toBeFalsy();
});
it('should pass other LogoutProps than "language" to signoutRedirect and convert "language" to an extraQueryParam', async () => {
const { userManager } = testData;
const signoutRedirectSpy = jest.spyOn(userManager, 'signoutRedirect');
- const loginParams: LogoutProps = {
+ const logoutParams: LogoutProps = {
extraQueryParams: { extraParam1: 'extra' },
state: { stateValue: 2, path: '/logout' },
id_token_hint: 'id_token_hint',
post_logout_redirect_uri: 'post_logout_redirect_uri',
};
- await waitForLogoutToTimeout({ ...loginParams, language: 'sv' });
+ await waitForLogoutToTimeout(mockedWindowControls, { ...logoutParams, language: 'sv' });
expect(signoutRedirectSpy).toHaveBeenNthCalledWith(1, {
- ...loginParams,
+ ...logoutParams,
extraQueryParams: {
- ...loginParams.extraQueryParams,
+ ...logoutParams.extraQueryParams,
ui_locales: 'sv',
},
});
@@ -158,7 +158,7 @@ describe('oidcClient', () => {
const userManagerProps = getDefaultOidcClientTestProps().userManagerSettings as UserManagerSettings;
const userFromStorage = getUserFromStorage(userManagerProps);
expect(userFromStorage).not.toBeNull();
- await waitForLogoutToTimeout();
+ await waitForLogoutToTimeout(mockedWindowControls);
const userFromStorageAfterLogout = getUserFromStorage(userManagerProps);
expect(userFromStorageAfterLogout).toBeNull();
});
@@ -445,9 +445,9 @@ describe('oidcClient', () => {
});
it('state changes when login is called. Payload has the state change', async () => {
await initTests({ modules: [listenerModule] });
- await waitForLoginToTimeout();
+ await waitForLoginToTimeout(mockedWindowControls);
const emittedSignals = getListenerSignals(listenerModule.getListener());
- expect(emittedSignals).toHaveLength(1);
+ expect(emittedSignals.length > 0).toBeTruthy();
expect(emittedSignals[0].type).toBe(stateChangeSignalType);
expect(emittedSignals[0].payload).toMatchObject({
state: oidcClientStates.LOGGING_IN,
@@ -457,7 +457,7 @@ describe('oidcClient', () => {
it('state changes when logout is called. Payload has the state change. USER_REMOVED event is emitted.', async () => {
placeUserToStorage();
await initTests({ modules: [listenerModule] });
- await waitForLogoutToTimeout();
+ await waitForLogoutToTimeout(mockedWindowControls);
const emittedSignals = getListenerSignals(listenerModule.getListener());
expect(emittedSignals).toHaveLength(2);
expect(emittedSignals[0].type).toBe(stateChangeSignalType);
diff --git a/packages/react/src/components/login/testUtils/oidcClientTestUtil.ts b/packages/react/src/components/login/testUtils/oidcClientTestUtil.ts
index 307b64fbcb..6c1f60f79b 100644
--- a/packages/react/src/components/login/testUtils/oidcClientTestUtil.ts
+++ b/packages/react/src/components/login/testUtils/oidcClientTestUtil.ts
@@ -18,6 +18,8 @@ import { createOidcClient } from '../client/oidcClient';
import openIdConfiguration from '../__mocks__/openIdConfiguration.json';
import { UserCreationProps, createSignInResponse, createUserAndPlaceUserToStorage } from './userTestUtil';
import { Beacon, ConnectedModule, createBeacon } from '../beacon/beacon';
+// eslint-disable-next-line jest/no-mocks-import
+import { MockedWindowLocationActions } from '../__mocks__/mockWindowLocation';
export type InitTestResult = {
oidcClient: OidcClient;
@@ -82,41 +84,24 @@ export function createOidcClientTestSuite() {
return Promise.reject(new Error(`Unknown url ${req.url}`));
});
- // oidcClient.login redirects the browser.
- // The returned promise is never resolved - unless an error occurs.
- // Always reject it here, no need for both fulfillments.
- async function waitForLoginToTimeout(loginProps?: LoginProps) {
- let promise: Promise
;
- await expect(async () =>
- waitFor(
- () => {
- if (!promise) {
- promise = oidcClient.login(loginProps);
- }
- return Promise.reject(new Error('Login redirected'));
- },
- {
- timeout: 1000,
- },
- ),
- ).rejects.toThrow();
+ async function waitForLoginToTimeout(mockedWindowControls: MockedWindowLocationActions, loginProps?: LoginProps) {
+ const currentCallCount = mockedWindowControls.getCalls().length;
+ oidcClient.login(loginProps).then(jest.fn()).catch(jest.fn());
+ await waitFor(() => {
+ if (mockedWindowControls.getCalls().length === currentCallCount) {
+ throw new Error('mockedWindowControls not called yet.');
+ }
+ });
}
- // loginClient.logout redirects the browser.
- // The returned promise is never resolved.
- async function waitForLogoutToTimeout(logoutProps?: LogoutProps) {
- let promise: Promise;
- await expect(() =>
- waitFor(
- () => {
- if (!promise) {
- promise = oidcClient.logout(logoutProps);
- }
- return promise;
- },
- { timeout: 1000 },
- ),
- ).rejects.toThrow();
+ async function waitForLogoutToTimeout(mockedWindowControls: MockedWindowLocationActions, logoutProps?: LogoutProps) {
+ const currentCallCount = mockedWindowControls.getCalls().length;
+ oidcClient.logout(logoutProps).then(jest.fn()).catch(jest.fn());
+ await waitFor(() => {
+ if (mockedWindowControls.getCalls().length === currentCallCount) {
+ throw new Error('mockedWindowControls not called yet.');
+ }
+ });
}
const initTests = async (
diff --git a/packages/react/src/components/login/whole.setup.test.ts b/packages/react/src/components/login/whole.setup.test.ts
index c2d33aaa18..3c03500338 100644
--- a/packages/react/src/components/login/whole.setup.test.ts
+++ b/packages/react/src/components/login/whole.setup.test.ts
@@ -21,7 +21,6 @@ import {
StateChangeSignalPayload,
stateChangeSignalType,
waitForSignals,
- errorSignalType,
} from './beacon/signals';
import { LISTEN_TO_ALL_MARKER, SignalNamespace, createBeacon } from './beacon/beacon';
import { ApiTokenClientProps, TokenData, apiTokensClientEvents, apiTokensClientNamespace } from './apiTokensClient';
@@ -47,6 +46,7 @@ type TestScenarioProps = {
userInStorageType?: UserInScenarios;
signInResponseType?: HttpStatusCode;
renewResponseType?: HttpStatusCode;
+ openIdConfigResponseType?: HttpStatusCode;
};
apiTokensClientProps: {
tokensInStorage?: ApiTokensInScenarios;
@@ -133,15 +133,6 @@ describe('Test all modules together', () => {
const openIdResponder: Responder = {
id: 'openIdConfig',
path: '/openid-configuration',
- responses: [
- {
- status: HttpStatusCode.OK,
- body: JSON.stringify(openIdConfiguration),
- headers: {
- 'content-type': 'application/json',
- },
- },
- ],
};
const responders: Responder[] = [apiTokensResponder, sessionPollerResponder, openIdResponder];
@@ -151,6 +142,7 @@ describe('Test all modules together', () => {
userInStorageType: 'none',
signInResponseType: HttpStatusCode.OK,
renewResponseType: HttpStatusCode.OK,
+ openIdConfigResponseType: HttpStatusCode.OK,
};
const defaultApiTokensClientProps: TestScenarioProps['apiTokensClientProps'] = {
tokensInStorage: 'none',
@@ -188,6 +180,21 @@ describe('Test all modules together', () => {
return null;
};
+ const setupOpenIdConfigResponse = (props: TestScenarioProps) => {
+ const { oidcClientProps } = props;
+ if (oidcClientProps.openIdConfigResponseType === HttpStatusCode.OK) {
+ const response = {
+ status: HttpStatusCode.OK,
+ body: JSON.stringify(openIdConfiguration),
+ headers: {
+ 'content-type': 'application/json',
+ },
+ };
+ addResponse(response, openIdResponder.id);
+ }
+ return null;
+ };
+
const setupSignInResponse = (props: TestScenarioProps): SigninResponse | null => {
const { oidcClientProps } = props;
if (oidcClientProps.signInResponseType === HttpStatusCode.OK) {
@@ -247,6 +254,7 @@ describe('Test all modules together', () => {
const initialUser = setupInitialUser(testProps);
const { oidcClient, userManager } = await initTests({});
+ setupOpenIdConfigResponse(testProps);
setupSignInResponse(testProps);
setupUserRenewalResponse(testProps, userManager);
@@ -362,19 +370,15 @@ describe('Test all modules together', () => {
});
it('When login starts, only login process starts', async () => {
const { getReceivedSignalTypes } = await initAll({});
- await waitForLoginToTimeout();
+ await waitForLoginToTimeout(mockedWindowControls);
// open id config is called on every login
jest.advanceTimersByTime(1000000);
expect(getRequestCount()).toBe(1);
expect(getRequestsInfoById(openIdResponder.id as string)).toHaveLength(1);
- expect(getReceivedSignalTypes(oidcClientNamespace)).toEqual([
- initSignalType,
- oidcClientStates.LOGGING_IN,
- errorSignalType,
- ]);
+ expect(getReceivedSignalTypes(oidcClientNamespace)).toEqual([initSignalType, oidcClientStates.LOGGING_IN]);
expect(getReceivedSignalTypes(apiTokensClientNamespace)).toEqual([initSignalType]);
expect(getReceivedSignalTypes(sessionPollerNamespace)).toEqual([initSignalType]);
- expect(getReceivedSignalTypes(LISTEN_TO_ALL_MARKER)).toHaveLength(8);
+ expect(getReceivedSignalTypes(LISTEN_TO_ALL_MARKER)).toHaveLength(7);
});
it('When login handleCallback is called and finished, apiTokens are fetched and session polling starts', async () => {
const { getReceivedSignalTypes, oidcClient, beacon } = await initAll({});
@@ -517,7 +521,7 @@ describe('Test all modules together', () => {
});
expect(mockMapForSessionHttpPoller.getCalls('start')).toHaveLength(1);
expect(mockMapForSessionHttpPoller.getCalls('stop')).toHaveLength(0);
- await waitForLogoutToTimeout();
+ await waitForLogoutToTimeout(mockedWindowControls);
expect(getReceivedSignalTypes(oidcClientNamespace)).toEqual([
initSignalType,
oidcClientStates.LOGGING_OUT,
diff --git a/packages/react/src/components/notification/Notification.tsx b/packages/react/src/components/notification/Notification.tsx
index b0f583ec46..544dcf9b76 100644
--- a/packages/react/src/components/notification/Notification.tsx
+++ b/packages/react/src/components/notification/Notification.tsx
@@ -2,9 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { animated, useSpring } from 'react-spring';
import { VisuallyHidden } from '@react-aria/visually-hidden';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import classNames from '../../utils/classNames';
import styles from './Notification.module.css';
import { IconInfoCircleFill, IconErrorFill, IconAlertCircleFill, IconCheckCircleFill, IconCross } from '../../icons';
diff --git a/packages/react/src/components/numberInput/NumberInput.tsx b/packages/react/src/components/numberInput/NumberInput.tsx
index c0d792af5d..4705a84116 100644
--- a/packages/react/src/components/numberInput/NumberInput.tsx
+++ b/packages/react/src/components/numberInput/NumberInput.tsx
@@ -2,8 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { VisuallyHidden } from '@react-aria/visually-hidden';
import useThrottledWheel from '../../hooks/useThrottledWheel';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './NumberInput.module.scss';
import { IconMinus, IconPlus } from '../../icons';
import { InputWrapper } from '../../internal/input-wrapper/InputWrapper';
diff --git a/packages/react/src/components/pagination/Pagination.tsx b/packages/react/src/components/pagination/Pagination.tsx
index 7d899d3500..5934c99662 100644
--- a/packages/react/src/components/pagination/Pagination.tsx
+++ b/packages/react/src/components/pagination/Pagination.tsx
@@ -1,8 +1,7 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { VisuallyHidden } from '@react-aria/visually-hidden';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './Pagination.module.scss';
import classNames from '../../utils/classNames';
import { Button } from '../button';
diff --git a/packages/react/src/components/passwordInput/PasswordInput.tsx b/packages/react/src/components/passwordInput/PasswordInput.tsx
index 889e26c7f8..fea96c7850 100644
--- a/packages/react/src/components/passwordInput/PasswordInput.tsx
+++ b/packages/react/src/components/passwordInput/PasswordInput.tsx
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './PasswordInput.module.scss';
import { TextInputProps } from '../textInput';
import { IconEye, IconEyeCrossed } from '../../icons';
diff --git a/packages/react/src/components/phoneInput/PhoneInput.tsx b/packages/react/src/components/phoneInput/PhoneInput.tsx
index 03123d21bb..6dbf870ca3 100644
--- a/packages/react/src/components/phoneInput/PhoneInput.tsx
+++ b/packages/react/src/components/phoneInput/PhoneInput.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import { InputWrapper } from '../../internal/input-wrapper/InputWrapper';
import { TextInputProps } from '../textInput';
import textInputStyles from '../textInput/TextInput.module.css';
diff --git a/packages/react/src/components/radioButton/RadioButton.tsx b/packages/react/src/components/radioButton/RadioButton.tsx
index f7c06ae15e..c5719da8b7 100644
--- a/packages/react/src/components/radioButton/RadioButton.tsx
+++ b/packages/react/src/components/radioButton/RadioButton.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './RadioButton.module.css';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/searchInput/SearchInput.tsx b/packages/react/src/components/searchInput/SearchInput.tsx
index 2aa1946044..603d29ff2e 100644
--- a/packages/react/src/components/searchInput/SearchInput.tsx
+++ b/packages/react/src/components/searchInput/SearchInput.tsx
@@ -1,9 +1,7 @@
import React, { KeyboardEvent, useState, useRef, useEffect } from 'react';
import { useCombobox, UseComboboxStateChangeTypes } from 'downshift';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './SearchInput.module.scss';
import { FieldLabel } from '../../internal/field-label/FieldLabel';
import { DropdownMenu } from '../../internal/dropdownMenu/DropdownMenu';
diff --git a/packages/react/src/components/section/Section.tsx b/packages/react/src/components/section/Section.tsx
index d05ded6385..96fe8edabd 100644
--- a/packages/react/src/components/section/Section.tsx
+++ b/packages/react/src/components/section/Section.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import classNames from '../../utils/classNames';
import { Koros, KorosType } from '../koros';
import styles from './Section.module.css';
diff --git a/packages/react/src/components/selectionGroup/SelectionGroup.tsx b/packages/react/src/components/selectionGroup/SelectionGroup.tsx
index 0b1ce6c8c9..0d67ec5a25 100644
--- a/packages/react/src/components/selectionGroup/SelectionGroup.tsx
+++ b/packages/react/src/components/selectionGroup/SelectionGroup.tsx
@@ -1,8 +1,6 @@
import React, { isValidElement, useEffect } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './SelectionGroup.module.scss';
import classNames from '../../utils/classNames';
import { RequiredIndicator } from '../../internal/required-indicator/RequiredIndicator';
diff --git a/packages/react/src/components/sideNavigation/SideNavigation.tsx b/packages/react/src/components/sideNavigation/SideNavigation.tsx
index 2801807064..651763a1f8 100644
--- a/packages/react/src/components/sideNavigation/SideNavigation.tsx
+++ b/packages/react/src/components/sideNavigation/SideNavigation.tsx
@@ -1,8 +1,6 @@
import React, { cloneElement, isValidElement, useEffect } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './SideNavigation.module.scss';
import SideNavigationContext from './SideNavigationContext';
import { FCWithName } from '../../common/types';
diff --git a/packages/react/src/components/statusLabel/StatusLabel.tsx b/packages/react/src/components/statusLabel/StatusLabel.tsx
index 4f3cb74242..8e5e0823a5 100644
--- a/packages/react/src/components/statusLabel/StatusLabel.tsx
+++ b/packages/react/src/components/statusLabel/StatusLabel.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './StatusLabel.module.css';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/stepByStep/StepByStep.tsx b/packages/react/src/components/stepByStep/StepByStep.tsx
index 4cdadf63f8..14d6a64b41 100644
--- a/packages/react/src/components/stepByStep/StepByStep.tsx
+++ b/packages/react/src/components/stepByStep/StepByStep.tsx
@@ -1,8 +1,6 @@
import React, { FC } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import { Button, ButtonProps } from '../button';
import classNames from '../../utils/classNames';
import { Link, LinkProps } from '../link';
diff --git a/packages/react/src/components/stepper/Step.tsx b/packages/react/src/components/stepper/Step.tsx
index 05c36dc952..9b55b5de74 100644
--- a/packages/react/src/components/stepper/Step.tsx
+++ b/packages/react/src/components/stepper/Step.tsx
@@ -1,8 +1,7 @@
import React from 'react';
+import '../../styles/base.module.css';
import { IconCheck, IconError, IconPlaybackPause } from '../../icons';
-// import base styles
-import '../../styles/base.css';
import styles from './Stepper.module.scss';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/stepper/Stepper.tsx b/packages/react/src/components/stepper/Stepper.tsx
index e1ec27bd1b..4c6bad5c64 100644
--- a/packages/react/src/components/stepper/Stepper.tsx
+++ b/packages/react/src/components/stepper/Stepper.tsx
@@ -1,7 +1,6 @@
import React, { createRef, useEffect, useRef, useState } from 'react';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './Stepper.module.scss';
import { Step, StepState } from './Step';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/table/Table.tsx b/packages/react/src/components/table/Table.tsx
index 8e467f94b3..02ad23e176 100644
--- a/packages/react/src/components/table/Table.tsx
+++ b/packages/react/src/components/table/Table.tsx
@@ -1,8 +1,6 @@
import React, { useMemo, useState } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Table.module.scss';
import { TableContainer } from './components/TableContainer';
import { HeaderRow } from './components/HeaderRow';
diff --git a/packages/react/src/components/table/components/SortingHeaderCell/SortingHeaderCell.tsx b/packages/react/src/components/table/components/SortingHeaderCell/SortingHeaderCell.tsx
index cf6c0fba71..f9b14d1873 100644
--- a/packages/react/src/components/table/components/SortingHeaderCell/SortingHeaderCell.tsx
+++ b/packages/react/src/components/table/components/SortingHeaderCell/SortingHeaderCell.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../../../styles/base.css';
-
+import '../../../../styles/base.module.css';
import styles from '../../Table.module.scss';
import {
IconSort,
diff --git a/packages/react/src/components/table/components/TableContainer/TableContainer.tsx b/packages/react/src/components/table/components/TableContainer/TableContainer.tsx
index bc07bf8358..e95ba1e8e8 100644
--- a/packages/react/src/components/table/components/TableContainer/TableContainer.tsx
+++ b/packages/react/src/components/table/components/TableContainer/TableContainer.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../../../styles/base.css';
-
+import '../../../../styles/base.module.css';
import classNames from '../../../../utils/classNames';
import styles from '../../Table.module.scss';
diff --git a/packages/react/src/components/tabs/Tab.tsx b/packages/react/src/components/tabs/Tab.tsx
index 7f3ab48abb..c6260c7e5f 100644
--- a/packages/react/src/components/tabs/Tab.tsx
+++ b/packages/react/src/components/tabs/Tab.tsx
@@ -1,8 +1,6 @@
import React, { useContext, useEffect, useRef } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Tabs.module.scss';
import classNames from '../../utils/classNames';
import { TabsContext } from './TabsContext';
diff --git a/packages/react/src/components/tabs/TabList.tsx b/packages/react/src/components/tabs/TabList.tsx
index 456274e9dd..74c8aeb814 100644
--- a/packages/react/src/components/tabs/TabList.tsx
+++ b/packages/react/src/components/tabs/TabList.tsx
@@ -1,8 +1,6 @@
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Tabs.module.scss';
import classNames from '../../utils/classNames';
import { FCWithName } from '../../common/types';
diff --git a/packages/react/src/components/tabs/TabPanel.tsx b/packages/react/src/components/tabs/TabPanel.tsx
index 764a66e6d9..8346200c9a 100644
--- a/packages/react/src/components/tabs/TabPanel.tsx
+++ b/packages/react/src/components/tabs/TabPanel.tsx
@@ -1,8 +1,6 @@
import React, { useContext } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import { TabsContext } from './TabsContext';
export type TabPanelProps = React.PropsWithChildren<{
diff --git a/packages/react/src/components/tabs/Tabs.tsx b/packages/react/src/components/tabs/Tabs.tsx
index 43cdc5457d..443633d762 100644
--- a/packages/react/src/components/tabs/Tabs.tsx
+++ b/packages/react/src/components/tabs/Tabs.tsx
@@ -1,8 +1,6 @@
import React, { useState } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Tabs.module.scss';
import classNames from '../../utils/classNames';
import { TabsContext } from './TabsContext';
diff --git a/packages/react/src/components/tag/Tag.tsx b/packages/react/src/components/tag/Tag.tsx
index dc9a3beb5d..031d4d8fba 100644
--- a/packages/react/src/components/tag/Tag.tsx
+++ b/packages/react/src/components/tag/Tag.tsx
@@ -1,8 +1,6 @@
-// eslint-disable-next-line import/order
import React, { forwardRef } from 'react';
-// import base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './Tag.module.scss';
import { IconCross } from '../../icons';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/textInput/TextInput.tsx b/packages/react/src/components/textInput/TextInput.tsx
index 0b6d2451ff..7e5f7f6b86 100644
--- a/packages/react/src/components/textInput/TextInput.tsx
+++ b/packages/react/src/components/textInput/TextInput.tsx
@@ -1,8 +1,6 @@
import React, { SyntheticEvent } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './TextInput.module.css';
import { InputWrapper } from '../../internal/input-wrapper/InputWrapper';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/components/textarea/TextArea.tsx b/packages/react/src/components/textarea/TextArea.tsx
index d04fa462b1..3bf4d85257 100644
--- a/packages/react/src/components/textarea/TextArea.tsx
+++ b/packages/react/src/components/textarea/TextArea.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from '../textInput/TextInput.module.css';
import { InputWrapper } from '../../internal/input-wrapper/InputWrapper';
import composeAriaDescribedBy from '../../utils/composeAriaDescribedBy';
diff --git a/packages/react/src/components/timeInput/TimeInput.tsx b/packages/react/src/components/timeInput/TimeInput.tsx
index 6c26f6097f..1359d7dc85 100644
--- a/packages/react/src/components/timeInput/TimeInput.tsx
+++ b/packages/react/src/components/timeInput/TimeInput.tsx
@@ -1,8 +1,6 @@
import React, { FocusEventHandler, useEffect, useRef, useState } from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import { InputWrapper } from '../../internal/input-wrapper/InputWrapper';
import { TextInputProps } from '../textInput/TextInput';
import textInputStyles from '../textInput/TextInput.module.css';
diff --git a/packages/react/src/components/toggleButton/ToggleButton.tsx b/packages/react/src/components/toggleButton/ToggleButton.tsx
index 9ec34beb18..82f1b20ef1 100644
--- a/packages/react/src/components/toggleButton/ToggleButton.tsx
+++ b/packages/react/src/components/toggleButton/ToggleButton.tsx
@@ -1,8 +1,6 @@
import React from 'react';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './ToggleButton.module.scss';
import classNames from '../../utils/classNames';
import { IconCrossCircleFill, IconCheckCircleFill } from '../../icons';
diff --git a/packages/react/src/components/tooltip/Tooltip.tsx b/packages/react/src/components/tooltip/Tooltip.tsx
index 89950f0fea..946f6f415c 100644
--- a/packages/react/src/components/tooltip/Tooltip.tsx
+++ b/packages/react/src/components/tooltip/Tooltip.tsx
@@ -2,9 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
import { Placement } from '@popperjs/core';
import { usePopper } from 'react-popper';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './Tooltip.module.scss';
import { IconQuestionCircle } from '../../icons/IconQuestionCircle';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/icons/IconBox.tsx b/packages/react/src/icons/IconBox.tsx
new file mode 100644
index 0000000000..534a2cdfea
--- /dev/null
+++ b/packages/react/src/icons/IconBox.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconBox = ({
+ ariaLabel = 'box',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/IconDocumentBlank.tsx b/packages/react/src/icons/IconDocumentBlank.tsx
new file mode 100644
index 0000000000..8297e864e0
--- /dev/null
+++ b/packages/react/src/icons/IconDocumentBlank.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconDocumentBlank = ({
+ ariaLabel = 'document-blank',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/IconDocumentBlankGroup.tsx b/packages/react/src/icons/IconDocumentBlankGroup.tsx
new file mode 100644
index 0000000000..883903cf42
--- /dev/null
+++ b/packages/react/src/icons/IconDocumentBlankGroup.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconDocumentBlankGroup = ({
+ ariaLabel = 'document-blank-group',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/IconDocumentGroup.tsx b/packages/react/src/icons/IconDocumentGroup.tsx
new file mode 100644
index 0000000000..1f82ab93b9
--- /dev/null
+++ b/packages/react/src/icons/IconDocumentGroup.tsx
@@ -0,0 +1,44 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconDocumentGroup = ({
+ ariaLabel = 'document-group',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/IconFolder.tsx b/packages/react/src/icons/IconFolder.tsx
new file mode 100644
index 0000000000..b54142446f
--- /dev/null
+++ b/packages/react/src/icons/IconFolder.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconFolder = ({
+ ariaLabel = 'folder',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/IconFolderGroup.tsx b/packages/react/src/icons/IconFolderGroup.tsx
new file mode 100644
index 0000000000..1b19eae6fe
--- /dev/null
+++ b/packages/react/src/icons/IconFolderGroup.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+
+import { IconProps } from './Icon.interface';
+import styles from './Icon.module.css';
+
+export const IconFolderGroup = ({
+ ariaLabel = 'folder-group',
+ ariaLabelledby,
+ ariaHidden = true,
+ className = '',
+ color,
+ size = 's',
+ style = {},
+}: React.SVGProps & IconProps) => (
+
+);
diff --git a/packages/react/src/icons/index.ts b/packages/react/src/icons/index.ts
index 8e4cd43660..bfa37617d4 100644
--- a/packages/react/src/icons/index.ts
+++ b/packages/react/src/icons/index.ts
@@ -22,6 +22,7 @@ export { IconBagCogwheel } from './IconBagCogwheel';
export { IconBell } from './IconBell';
export { IconBellCrossed } from './IconBellCrossed';
export { IconBinoculars } from './IconBinoculars';
+export { IconBox } from './IconBox';
export { IconCake } from './IconCake';
export { IconCalendar } from './IconCalendar';
export { IconCalendarClock } from './IconCalendarClock';
@@ -52,6 +53,9 @@ export { IconCustomerBotPositive } from './IconCustomerBotPositive';
export { IconDiscord } from './IconDiscord';
export { IconDisplay } from './IconDisplay';
export { IconDocument } from './IconDocument';
+export { IconDocumentBlank } from './IconDocumentBlank';
+export { IconDocumentBlankGroup } from './IconDocumentBlankGroup';
+export { IconDocumentGroup } from './IconDocumentGroup';
export { IconDownload } from './IconDownload';
export { IconDownloadCloud } from './IconDownloadCloud';
export { IconDrag } from './IconDrag';
@@ -67,6 +71,8 @@ export { IconFaceSad } from './IconFaceSad';
export { IconFaceSmile } from './IconFaceSmile';
export { IconFacebook } from './IconFacebook';
export { IconFamily } from './IconFamily';
+export { IconFolder } from './IconFolder';
+export { IconFolderGroup } from './IconFolderGroup';
export { IconGlobe } from './IconGlobe';
export { IconGoogle } from './IconGoogle';
export { IconGraphColumns } from './IconGraphColumns';
diff --git a/packages/react/src/internal/menuButton/MenuButton.tsx b/packages/react/src/internal/menuButton/MenuButton.tsx
index d53580240d..0fd81a1d40 100644
--- a/packages/react/src/internal/menuButton/MenuButton.tsx
+++ b/packages/react/src/internal/menuButton/MenuButton.tsx
@@ -4,9 +4,7 @@ import mergeRefs from 'react-merge-refs';
import useMeasure from 'react-use-measure';
import { ResizeObserver } from '@juggle/resize-observer';
-// import base styles
-import '../../styles/base.css';
-
+import '../../styles/base.module.css';
import styles from './MenuButton.module.scss';
import { Menu } from './menu/Menu';
import { IconAngleDown, IconAngleUp } from '../../icons';
diff --git a/packages/react/src/internal/skipLink/SkipLink.tsx b/packages/react/src/internal/skipLink/SkipLink.tsx
index 383005cf12..656e489d00 100644
--- a/packages/react/src/internal/skipLink/SkipLink.tsx
+++ b/packages/react/src/internal/skipLink/SkipLink.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-// import core base styles
-import '../../styles/base.css';
+import '../../styles/base.module.css';
import styles from './SkipLink.module.scss';
import { useTheme } from '../../hooks/useTheme';
import classNames from '../../utils/classNames';
diff --git a/packages/react/src/styles/base.css b/packages/react/src/styles/base.css
deleted file mode 100644
index 5812a8f3e9..0000000000
--- a/packages/react/src/styles/base.css
+++ /dev/null
@@ -1,12 +0,0 @@
-@import 'hds-design-tokens/lib/all.css';
-@import 'hds-core/lib/utils/animations.css';
-@import 'hds-core/lib/utils/helpers.css';
-
-/*
- * Normalize.css rule
- * 1. Prevent adjustments of font size after orientation changes in iOS.
- */
-
-html {
- -webkit-text-size-adjust: 100%; /* 1 */
-}
diff --git a/packages/react/src/styles/base.module.css b/packages/react/src/styles/base.module.css
new file mode 100644
index 0000000000..6fd0b913c0
--- /dev/null
+++ b/packages/react/src/styles/base.module.css
@@ -0,0 +1 @@
+@import 'hds-core/lib/base.css';
\ No newline at end of file
diff --git a/release/hds-icon-kit.zip b/release/hds-icon-kit.zip
index 085c783184..972fc6ce06 100644
Binary files a/release/hds-icon-kit.zip and b/release/hds-icon-kit.zip differ
diff --git a/release/icon-kit-template-CHANGELOG.txt b/release/icon-kit-template-CHANGELOG.txt
index 833a784517..565c613887 100755
--- a/release/icon-kit-template-CHANGELOG.txt
+++ b/release/icon-kit-template-CHANGELOG.txt
@@ -1,4 +1,4 @@
-# Helsinki Design System, Icon kit, version 3.3.0
+# Helsinki Design System, Icon kit, version 3.4.0
## CHANGELOG
diff --git a/site/package.json b/site/package.json
index 24f9cc5ab0..ddb4119a94 100644
--- a/site/package.json
+++ b/site/package.json
@@ -2,7 +2,7 @@
"name": "site",
"private": true,
"description": "Documentation for Helsinki Design System",
- "version": "3.4.0",
+ "version": "3.5.0",
"workspaces": {
"nohoist": [
"gatsby",
@@ -46,9 +46,9 @@
"chalk": "4.0.0",
"crypto-browserify": "^3.11.0",
"eslint-config-react-app": "^7.0.1",
- "hds-core": "3.4.0",
- "hds-design-tokens": "3.4.0",
- "hds-react": "3.4.0",
+ "hds-core": "3.5.0",
+ "hds-design-tokens": "3.5.0",
+ "hds-react": "3.5.0",
"inquirer": "7.1.0",
"postcss": "8",
"prettier": "2.5.1",
diff --git a/site/src/components/layout.js b/site/src/components/layout.js
index 2dad607b21..4e7730b006 100644
--- a/site/src/components/layout.js
+++ b/site/src/components/layout.js
@@ -5,7 +5,6 @@
* See: https://www.gatsbyjs.com/docs/use-static-query/
*/
-import 'hds-core';
import * as React from 'react';
import PropTypes from 'prop-types';
import { useStaticQuery, graphql, withPrefix, Link as GatsbyLink, navigate } from 'gatsby';
diff --git a/site/src/docs/components/hero/code.mdx b/site/src/docs/components/hero/code.mdx
index 2e7722d8f3..675d83c11f 100644
--- a/site/src/docs/components/hero/code.mdx
+++ b/site/src/docs/components/hero/code.mdx
@@ -107,6 +107,105 @@ import { Fieldset, TextInput } from 'hds-react';
+#### Diagonal koros with arrow
+
+Hero has an option to show an arrow icon to indicate the followin content. The arrow color can also be customised with the `theme` prop by passing `--arrow-icon-color` in it.
+
+
+
+```jsx
+import { Fieldset, TextInput } from 'hds-react';
+
+() => (
+
+ Hero title
+
+ This is an introduction text. The length is circa two sentences. The suitable total length of the title and
+ introduction is around 230 characters, including spaces. The intro ends with a period.
+
+
+
+)
+```
+
+```html
+
+
+
+
+
+
+
+
Hero title
+
+ This is an introduction text. The length is circa two sentences. The suitable total length of the title
+ and introduction is around 230 characters, including spaces. The intro ends with a period.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
**Note:** Hero with diagonal Koros adjusts its contents based on browser width. Preview here might might not scale correcly. View Hero with diagonal Koros example in Storybook.
### Packages
@@ -127,3 +226,4 @@ Note! You can find the full list of properties in the
Hero title
@@ -74,6 +77,7 @@ import { Hero } from 'hds-react';
-
+
@@ -113,6 +117,9 @@ import { Hero } from 'hds-react';
+
+
+
```
diff --git a/site/src/docs/components/hero/index.mdx b/site/src/docs/components/hero/index.mdx
index fd49003cc0..66f050906a 100644
--- a/site/src/docs/components/hero/index.mdx
+++ b/site/src/docs/components/hero/index.mdx
@@ -28,9 +28,7 @@ export default ({ children, pageContext }) =>
- Call to action
-
+ >Call to action
@@ -51,6 +49,7 @@ export default ({ children, pageContext }) => HDS Koros documentation.
- **Title and introduction (required):** The suitable total length of the title and introduction is circa 230 characters, including spaces. The intro ends with a period.
- **CTA:** Buttons can also be added to the Hero component, and it is recommended to include, e.g. the most critical link of the page in the Hero component, if one is known. By default, use a button with a background colour (primary button style). On dark background colours, secondary button style can be used.
+- **Arrow icon:** Hero can contain an arrow icon on the bottom left corner to indicate there's more content. The arrow slightly overlaps both the Hero and the following content. Can be used on all Hero variants and the color can be changed in Hero's theme.
### React subcomponents
@@ -84,9 +83,7 @@ Use when you want to support the title's message with an image. Don't use an ima
variant="secondary"
role="link"
style={{ '--background-color': '#000', '--color': '#fff', '--border-color': '#000' }}
- >
- Call to action
-
+ >Call to action
@@ -110,9 +107,7 @@ Use when you want to fill the background area with an image. Use an image that w
variant="secondary"
role="link"
style={{ '--background-color': '#000', '--color': '#fff', '--border-color': '#000' }}
- >
- Call to action
-
+ >Call to action
@@ -138,9 +133,7 @@ Use when you want to create a dynamic Hero layout on your page. Ensure the image
variant="secondary"
role="link"
style={{ '--background-color': '#000', '--color': '#fff', '--border-color': '#000' }}
- >
- Call to action
-
+ >Call to action
@@ -166,9 +159,7 @@ Use when you want to emphasize the title and introduction text content over the
variant="secondary"
role="link"
style={{ '--background-color': '#000', '--color': '#fff', '--border-color': '#000' }}
- >
- Call to action
-
+ >Call to action
@@ -192,8 +183,31 @@ Use when you don't have a suitable image to support the title section's message
variant="secondary"
role="link"
style={{ '--background-color': '#000', '--color': '#fff', '--border-color': '#000' }}
- >
- Call to action
-
+ >Call to action
+
+#### Arrow icon
+
+Used to indicate there's more content to follow.
+
+
+
+ Hero with an arrow icon
+ This is an introduction text. The length is circa two sentences. The suitable total length of the title and introduction is around 230 characters, including spaces. The intro ends with a period.
+
+
+
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 0635912f3e..85f286e3d5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -20591,11 +20591,6 @@ normalize-url@^8.0.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.0.tgz#593dbd284f743e8dcf6a5ddf8fadff149c82701a"
integrity sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==
-normalize.css@8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
- integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
-
not@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d"