@altinn/altinn-components 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/lib/components/Footer/Footer.stories.ts +37 -0
- package/lib/components/Footer/Footer.tsx +21 -0
- package/lib/components/Footer/FooterAddress.tsx +12 -0
- package/lib/components/Footer/FooterBase.tsx +16 -0
- package/lib/components/Footer/FooterLogo.tsx +17 -0
- package/lib/components/Footer/FooterMenu.tsx +43 -0
- package/lib/components/Footer/footerAddress.module.css +8 -0
- package/lib/components/Footer/footerBase.module.css +28 -0
- package/lib/components/Footer/footerLogo.module.css +12 -0
- package/lib/components/Footer/footerMenu.module.css +30 -0
- package/lib/components/Footer/index.ts +1 -0
- package/lib/components/Layout/Layout.stories.tsx +30 -2
- package/lib/components/Layout/Layout.tsx +4 -2
- package/lib/components/index.ts +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/Altinn/altinn-components/compare/v0.3.0...v0.4.0) (2024-11-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add footer component ([#40](https://github.com/Altinn/altinn-components/issues/40)) ([bdf5c0c](https://github.com/Altinn/altinn-components/commit/bdf5c0cd68c374ac5fe83536033074289f379abe))
|
|
9
|
+
|
|
3
10
|
## [0.3.0](https://github.com/Altinn/altinn-components/compare/v0.2.2...v0.3.0) (2024-11-08)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Footer } from './Footer';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Footer/Footer',
|
|
6
|
+
component: Footer,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {},
|
|
9
|
+
args: {
|
|
10
|
+
address: 'Postboks 1382 Vika, 0114 Oslo.',
|
|
11
|
+
menu: {
|
|
12
|
+
items: [
|
|
13
|
+
{
|
|
14
|
+
id: '1',
|
|
15
|
+
title: 'Om Altinn',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: '2',
|
|
19
|
+
title: 'Driftsmeldinger',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: '3',
|
|
23
|
+
title: 'Personvern',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: '4',
|
|
27
|
+
title: 'Tilgjengelighet',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
} satisfies Meta<typeof Footer>;
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
type Story = StoryObj<typeof meta>;
|
|
36
|
+
|
|
37
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FooterAddress } from './FooterAddress';
|
|
2
|
+
import { FooterBase } from './FooterBase';
|
|
3
|
+
import { FooterLogo } from './FooterLogo';
|
|
4
|
+
import { FooterMenu, type FooterMenuProps } from './FooterMenu';
|
|
5
|
+
|
|
6
|
+
export interface FooterProps {
|
|
7
|
+
address: string;
|
|
8
|
+
menu: FooterMenuProps;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Footer = ({ address, menu }: FooterProps) => {
|
|
12
|
+
return (
|
|
13
|
+
<FooterBase>
|
|
14
|
+
<FooterAddress>
|
|
15
|
+
<FooterLogo />
|
|
16
|
+
{address}
|
|
17
|
+
</FooterAddress>
|
|
18
|
+
<FooterMenu {...menu} />
|
|
19
|
+
</FooterBase>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import styles from './footerAddress.module.css';
|
|
4
|
+
|
|
5
|
+
export interface FooterAddressProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FooterAddress = ({ className, children }: FooterAddressProps) => {
|
|
11
|
+
return <address className={cx(styles.address, className)}>{children}</address>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import styles from './footerBase.module.css';
|
|
4
|
+
|
|
5
|
+
export interface FooterBaseProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FooterBase = ({ className, children }: FooterBaseProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<footer className={cx(styles.footer, className)}>
|
|
13
|
+
<div className={styles.grid}>{children} </div>
|
|
14
|
+
</footer>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import { DigdirLogomark } from '../Header/DigdirLogomark.tsx';
|
|
3
|
+
import styles from './footerLogo.module.css';
|
|
4
|
+
|
|
5
|
+
export interface FooterLogoProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FooterLogo = ({ className, title = 'Digdir' }: FooterLogoProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<div className={cx(styles.logo, className)}>
|
|
13
|
+
<DigdirLogomark className={styles.symbol} />
|
|
14
|
+
<span>{title}</span>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import cx from 'classnames';
|
|
2
|
+
import type { ElementType } from 'react';
|
|
3
|
+
import { MenuBase } from '../Menu/';
|
|
4
|
+
import styles from './footerMenu.module.css';
|
|
5
|
+
|
|
6
|
+
export interface FooterMenuProps {
|
|
7
|
+
className?: string;
|
|
8
|
+
items: FooterLinkProps[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FooterLinkProps {
|
|
12
|
+
as?: ElementType;
|
|
13
|
+
id?: string;
|
|
14
|
+
href?: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const FooterLink = ({ as, className, title, ...rest }: FooterLinkProps) => {
|
|
20
|
+
const Component = as || 'a';
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Component className={cx(styles.link, className)} {...rest}>
|
|
24
|
+
{title}
|
|
25
|
+
</Component>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const FooterMenu = ({ className, items = [] }: FooterMenuProps) => {
|
|
30
|
+
return (
|
|
31
|
+
<MenuBase className={cx(styles.menu, className)}>
|
|
32
|
+
<ul className={styles.list}>
|
|
33
|
+
{items.map((item) => {
|
|
34
|
+
return (
|
|
35
|
+
<li key={item.id}>
|
|
36
|
+
<FooterLink {...item} />
|
|
37
|
+
</li>
|
|
38
|
+
);
|
|
39
|
+
})}
|
|
40
|
+
</ul>
|
|
41
|
+
</MenuBase>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.footer {
|
|
2
|
+
border-top: 1px solid;
|
|
3
|
+
border-color: var(--theme-border-subtle);
|
|
4
|
+
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
row-gap: 1rem;
|
|
8
|
+
|
|
9
|
+
padding: 1rem 0;
|
|
10
|
+
margin: 2rem 1rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.grid {
|
|
14
|
+
width: 100%;
|
|
15
|
+
max-width: 80rem;
|
|
16
|
+
margin: 0 auto;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
justify-content: space-between;
|
|
20
|
+
row-gap: 1rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media (min-width: 1024px) {
|
|
24
|
+
.grid {
|
|
25
|
+
padding: 0 1rem;
|
|
26
|
+
flex-direction: row;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.menu {
|
|
2
|
+
position: relative;
|
|
3
|
+
font-size: .875rem;
|
|
4
|
+
line-height: 1.5;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.list {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
row-gap: 1rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@media (min-width: 1024px) {
|
|
14
|
+
.list {
|
|
15
|
+
flex-direction: row;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: start;
|
|
18
|
+
column-gap: .5rem;
|
|
19
|
+
width: auto;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.list .item:hover {
|
|
24
|
+
background-color: var(--global-base-hover);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.link {
|
|
28
|
+
color: inherit;
|
|
29
|
+
text-decoration: underline;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Footer';
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { ActionFooter, ActionHeader, ActionMenu, DialogListItem, ListBase, Snackbar } from '../';
|
|
4
|
-
import type { HeaderProps } from '../
|
|
5
|
-
import type { MenuProps } from '../Menu';
|
|
4
|
+
import type { FooterProps, HeaderProps, MenuProps } from '../';
|
|
6
5
|
import { Layout } from './Layout';
|
|
7
6
|
|
|
8
7
|
const header: HeaderProps = {
|
|
@@ -21,6 +20,34 @@ const header: HeaderProps = {
|
|
|
21
20
|
},
|
|
22
21
|
};
|
|
23
22
|
|
|
23
|
+
const footer: FooterProps = {
|
|
24
|
+
address: 'Postboks 1382 Vika, 0114 Oslo.',
|
|
25
|
+
menu: {
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
id: '1',
|
|
29
|
+
href: '#',
|
|
30
|
+
title: 'Om Altinn',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: '2',
|
|
34
|
+
href: '#',
|
|
35
|
+
title: 'Driftsmeldinger',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: '3',
|
|
39
|
+
href: '#',
|
|
40
|
+
title: 'Personvern',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: '4',
|
|
44
|
+
href: '#',
|
|
45
|
+
title: 'Tilgjengelighet',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
24
51
|
const menu: MenuProps = {
|
|
25
52
|
groups: {},
|
|
26
53
|
items: [
|
|
@@ -76,6 +103,7 @@ const meta = {
|
|
|
76
103
|
},
|
|
77
104
|
args: {
|
|
78
105
|
header,
|
|
106
|
+
footer,
|
|
79
107
|
sidebar: {
|
|
80
108
|
menu,
|
|
81
109
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { LayoutBase, LayoutBody, LayoutContent, LayoutSidebar, type LayoutTheme } from '.';
|
|
3
|
+
import { Footer, type FooterProps } from '../Footer';
|
|
3
4
|
import { Header, type HeaderProps } from '../Header';
|
|
4
5
|
import { Menu, type MenuProps } from '../Menu';
|
|
5
6
|
|
|
@@ -17,12 +18,13 @@ interface ContentProps {
|
|
|
17
18
|
export interface LayoutProps {
|
|
18
19
|
theme?: LayoutTheme;
|
|
19
20
|
header?: HeaderProps;
|
|
21
|
+
footer?: FooterProps;
|
|
20
22
|
sidebar: SidebarProps;
|
|
21
23
|
content: ContentProps;
|
|
22
24
|
children: ReactNode;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
export const Layout = ({ theme = 'global', header, sidebar = {}, content = {}, children }: LayoutProps) => {
|
|
27
|
+
export const Layout = ({ theme = 'global', header, footer, sidebar = {}, content = {}, children }: LayoutProps) => {
|
|
26
28
|
const { menu, ...sideRestProps } = sidebar;
|
|
27
29
|
return (
|
|
28
30
|
<LayoutBase theme={theme}>
|
|
@@ -32,11 +34,11 @@ export const Layout = ({ theme = 'global', header, sidebar = {}, content = {}, c
|
|
|
32
34
|
<LayoutSidebar hidden={sidebar?.hidden} theme={sidebar?.theme} {...sideRestProps}>
|
|
33
35
|
{menu && <Menu {...menu} />}
|
|
34
36
|
{sidebar?.children}
|
|
35
|
-
{sidebar?.hidden && 'HIDDEN'}
|
|
36
37
|
</LayoutSidebar>
|
|
37
38
|
)}
|
|
38
39
|
<LayoutContent theme={content?.theme}>{children}</LayoutContent>
|
|
39
40
|
</LayoutBody>
|
|
41
|
+
{footer && <Footer {...footer} />}
|
|
40
42
|
</LayoutBase>
|
|
41
43
|
);
|
|
42
44
|
};
|
package/lib/components/index.ts
CHANGED