From d61bf336282cdddc496e1393bbacad15233b42d3 Mon Sep 17 00:00:00 2001 From: Alexander Midteide <144693023+alexdigdir@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:33:29 +0100 Subject: [PATCH] Added Header component for desktop (#185) --- .gitignore | 2 +- .../src/components/Header/AltinnLogo.tsx | 19 ++++ .../src/components/Header/Header.module.css | 106 ++++++++++++++++++ .../src/components/Header/Header.tsx | 73 ++++++++++++ packages/storybook/.storybook/main.ts | 29 ++--- packages/storybook/package.json | 8 +- packages/storybook/src/main.tsx | 23 ++-- .../src/stories/Header/Header.stories.ts | 25 +++++ pnpm-lock.yaml | 51 ++++++++- 9 files changed, 307 insertions(+), 29 deletions(-) create mode 100644 packages/frontend-design-poc/src/components/Header/AltinnLogo.tsx create mode 100644 packages/frontend-design-poc/src/components/Header/Header.module.css create mode 100644 packages/frontend-design-poc/src/components/Header/Header.tsx create mode 100644 packages/storybook/src/stories/Header/Header.stories.ts diff --git a/.gitignore b/.gitignore index 7e4f3476b..c1f6a5520 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ pg* *.env* .DS* - +*vscode # Created by https://www.toptal.com/developers/gitignore/api/macos,windows,linux,visualstudiocode,vim,webstorm+all,node,yarn # Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,linux,visualstudiocode,vim,webstorm+all,node,yarn diff --git a/packages/frontend-design-poc/src/components/Header/AltinnLogo.tsx b/packages/frontend-design-poc/src/components/Header/AltinnLogo.tsx new file mode 100644 index 000000000..5113713b0 --- /dev/null +++ b/packages/frontend-design-poc/src/components/Header/AltinnLogo.tsx @@ -0,0 +1,19 @@ +export default function AltinnLogo() { + return ( + + Altinn logo + + + + + + + + + + ); +} diff --git a/packages/frontend-design-poc/src/components/Header/Header.module.css b/packages/frontend-design-poc/src/components/Header/Header.module.css new file mode 100644 index 000000000..d852f8b8c --- /dev/null +++ b/packages/frontend-design-poc/src/components/Header/Header.module.css @@ -0,0 +1,106 @@ +.navigation { + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + background: #E5EBEC; + /* Må byttes til variabel */ + padding: 24px; + max-height: 68px; +} + +.logo a { + color: #000; + text-align: center; + font-size: 1.5rem; + font-style: normal; + font-weight: 500; + line-height: 32px; + text-decoration: none; + display: flex; + align-items: center; +} + +.logoText { + margin-left: 6px; +} + +.searchBar { + margin: 0 42px; + + input { + border-radius: 28px; + border: 2px solid black; + } +} + + + +.userSection { + display: flex; + align-items: center; +} + +.userProfile { + display: flex; + align-items: center; + margin-left: 20px; +} + +.userProfile span { + margin-right: 10px; +} + + +.nameWithInitials { + display: flex; + align-items: center; + gap: 10px; + padding-right: 22px; +} + + +.primaryName { + text-align: center; + color: #000; + text-align: center; + font-family: Inter; + font-size: 18px; + font-style: normal; + font-weight: 500; + line-height: 28px; + /* 155.556% */ +} + +.secondaryName { + color: #000; + text-align: center; + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 150%; +} + +.companyContainer { + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.initialsCircle { + min-width: 48px; + min-height: 48px; + background-color: var(--fds-semantic-surface-success-default); + color: white; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + font-size: 20px; +} + +.initialsCircle.isOrganization { + border-radius: 4px; + background: var(--Bedrift-Primary, #008FD6); +} \ No newline at end of file diff --git a/packages/frontend-design-poc/src/components/Header/Header.tsx b/packages/frontend-design-poc/src/components/Header/Header.tsx new file mode 100644 index 000000000..f489a8938 --- /dev/null +++ b/packages/frontend-design-poc/src/components/Header/Header.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import styles from './Header.module.css'; // Import the CSS module +import { Search } from '@digdir/design-system-react'; +import AltinnLogo from './AltinnLogo'; +import cx from 'classnames'; + +/** + * Simple header component with a logo, search bar, and user name with initials. + * + * + * @component + * @param {string} props.name - The name of the user. + * @param {string} props.companyName - The name of the company the user is associated with if any. + * @returns {JSX.Element} The header component. + * + * @example + * + + */ + +export type HeaderProps = { + name: string; + companyName?: string; +}; + +const getInitials = (name: string, companyName?: string) => { + if (!companyName) + return name + .split(' ') + .map((n) => n[0]) + .join(''); + return companyName[0]; +}; + +export const Header: React.FC = ({ name, companyName }) => { + return ( + + + + + + Altinn + + + + + + {companyName ? ( + + + {companyName} + {name} + + + {getInitials(name, companyName)} + + + ) : ( + + {name} + + {getInitials(name, companyName)} + + + )} + + + ); +}; diff --git a/packages/storybook/.storybook/main.ts b/packages/storybook/.storybook/main.ts index 28bd15dc4..8eb38bd56 100644 --- a/packages/storybook/.storybook/main.ts +++ b/packages/storybook/.storybook/main.ts @@ -1,19 +1,20 @@ import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { - stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-onboarding', - '@storybook/addon-interactions', - ], - framework: { - name: '@storybook/react-vite', - options: {}, - }, - docs: { - autodocs: 'tag', - }, + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-onboarding', + 'storybook-addon-react-router-v6', + '@storybook/addon-interactions', + ], + framework: { + name: '@storybook/react-vite', + options: {}, + }, + docs: { + autodocs: 'tag', + }, }; export default config; diff --git a/packages/storybook/package.json b/packages/storybook/package.json index f44dde644..be6ca24e8 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -15,7 +15,8 @@ "@digdir/design-system-tokens": "^0.12.0", "@navikt/aksel-icons": "^5.18.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.16.0" }, "resolutions": { "jackspeak": "2.1.1" @@ -23,18 +24,19 @@ "devDependencies": { "@storybook/addon-essentials": "^7.6.12", "@storybook/addon-interactions": "^7.6.12", - "@storybook/theming": "^7.6.6", - "@storybook/manager-api": "^7.6.6", "@storybook/addon-links": "^7.6.12", "@storybook/addon-onboarding": "^1.0.11", "@storybook/blocks": "^7.6.12", + "@storybook/manager-api": "^7.6.6", "@storybook/react": "^7.6.12", "@storybook/react-vite": "^7.6.12", "@storybook/test": "^7.6.12", + "@storybook/theming": "^7.6.6", "@types/react": "^18.2.43", "@types/react-dom": "^18.2.17", "@vitejs/plugin-react": "^4.2.1", "storybook": "^7.6.12", + "storybook-addon-react-router-v6": "^2.0.10", "typescript": "^5.2.2", "vite": "^5.0.8" } diff --git a/packages/storybook/src/main.tsx b/packages/storybook/src/main.tsx index 95e2bdc2c..f542b8e26 100644 --- a/packages/storybook/src/main.tsx +++ b/packages/storybook/src/main.tsx @@ -1,9 +1,16 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; +import ReactDOM from 'react-dom/client'; +import App from './App'; // Ensure this is the correct path, and typically you don't include the file extension +import { BrowserRouter } from 'react-router-dom'; -ReactDOM.createRoot(document.getElementById("root")!).render( - - - -); +const rootElement = document.getElementById('root'); + +if (rootElement) { + const root = ReactDOM.createRoot(rootElement); + root.render( + + + , + ); +} else { + console.error('Failed to find the root element'); +} diff --git a/packages/storybook/src/stories/Header/Header.stories.ts b/packages/storybook/src/stories/Header/Header.stories.ts new file mode 100644 index 000000000..0e6c1165f --- /dev/null +++ b/packages/storybook/src/stories/Header/Header.stories.ts @@ -0,0 +1,25 @@ +import { Header, HeaderProps } from '../../../../frontend-design-poc/src/components/Header/Header'; +import { Meta, StoryObj } from '@storybook/react'; +import { withRouter } from 'storybook-addon-react-router-v6'; + +export default { + title: 'Components/Header', + component: Header, + decorators: [withRouter], + parameters: { + layout: 'fullscreen', + }, +} as Meta; + +export const Default: StoryObj = { + args: { + name: 'Ola Nordmann', + }, +}; + +export const WithCompanyName: StoryObj = { + args: { + name: 'Ola Nordmann', + companyName: 'Aker Solutions AS', + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd0e3ddb4..92bc92044 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,6 +399,9 @@ importers: storybook: specifier: ^7.6.12 version: 7.6.12 + storybook-addon-react-router-v6: + specifier: ^2.0.10 + version: 2.0.10(@storybook/blocks@7.6.12)(@storybook/channels@7.6.12)(@storybook/components@7.6.12)(@storybook/core-events@7.6.12)(@storybook/manager-api@7.6.12)(@storybook/preview-api@7.6.12)(@storybook/theming@7.6.12)(react-dom@18.2.0)(react-router-dom@6.21.3)(react@18.2.0) typescript: specifier: ^5.2.2 version: 5.3.3 @@ -4023,7 +4026,6 @@ packages: /@remix-run/router@1.14.2: resolution: {integrity: sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==} engines: {node: '>=14.0.0'} - dev: false /@rollup/plugin-babel@5.3.1(@babel/core@7.23.9)(rollup@2.79.1): resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} @@ -7676,6 +7678,10 @@ packages: /commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + /compare-versions@6.1.0: + resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==} + dev: true + /compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -14110,6 +14116,14 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /react-inspector@6.0.2(react@18.2.0): + resolution: {integrity: sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==} + peerDependencies: + react: ^16.8.4 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: true + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -14208,7 +14222,6 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-router: 6.21.3(react@18.2.0) - dev: false /react-router@6.21.3(react@18.2.0): resolution: {integrity: sha512-a0H638ZXULv1OdkmiK6s6itNhoy33ywxmUFT/xtSoVyf9VnC7n7+VT4LjVzdIHSaF5TIh9ylUgxMXksHTgGrKg==} @@ -14218,7 +14231,6 @@ packages: dependencies: '@remix-run/router': 1.14.2 react: 18.2.0 - dev: false /react-scripts@5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(@swc/core@1.3.107)(eslint@8.56.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.4): resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==} @@ -15196,6 +15208,39 @@ packages: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} dev: true + /storybook-addon-react-router-v6@2.0.10(@storybook/blocks@7.6.12)(@storybook/channels@7.6.12)(@storybook/components@7.6.12)(@storybook/core-events@7.6.12)(@storybook/manager-api@7.6.12)(@storybook/preview-api@7.6.12)(@storybook/theming@7.6.12)(react-dom@18.2.0)(react-router-dom@6.21.3)(react@18.2.0): + resolution: {integrity: sha512-HnrjbujAW1oGCdgvv7W6QKskETNXvt0U5qR2pKD8GXHlqkV9wmWa6HW3J0ks0aiBJBsRrPdEViatrhnRD5RZZw==} + peerDependencies: + '@storybook/blocks': ^7.0.0 + '@storybook/channels': ^7.0.0 + '@storybook/components': ^7.0.0 + '@storybook/core-events': ^7.0.0 + '@storybook/manager-api': ^7.0.0 + '@storybook/preview-api': ^7.0.0 + '@storybook/theming': ^7.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-router-dom: ^6.4.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/blocks': 7.6.12(@types/react-dom@18.2.18)(@types/react@18.2.51)(react-dom@18.2.0)(react@18.2.0) + '@storybook/channels': 7.6.12 + '@storybook/components': 7.6.12(@types/react-dom@18.2.18)(@types/react@18.2.51)(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.6.12 + '@storybook/manager-api': 7.6.12(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.6.12 + '@storybook/theming': 7.6.12(react-dom@18.2.0)(react@18.2.0) + compare-versions: 6.1.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-inspector: 6.0.2(react@18.2.0) + react-router-dom: 6.21.3(react-dom@18.2.0)(react@18.2.0) + dev: true + /storybook@7.6.12: resolution: {integrity: sha512-zcH9CwIsE8N4PX3he5vaJ3mTTWGxu7cxJ/ag9oja/k3N5/IvQjRyIU1TLkRVb28BB8gaLyorpnc4C4aLVGy4WQ==} hasBin: true