@fellesdatakatalog/ui 1.0.15 → 1.0.17
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/dist/shared-ui.cjs.map +1 -1
- package/dist/shared-ui.es.js.map +1 -1
- package/package.json +33 -24
- package/src/components/app-bar/app-bar.stories.tsx +22 -0
- package/src/components/app-bar/index.tsx +12 -0
- package/src/components/app-bar/styles.module.scss +7 -0
- package/src/components/checkbox-group/checkbox-group.stories.tsx +143 -0
- package/src/components/checkbox-group/index.tsx +105 -0
- package/src/components/copy-button/copy-button.stories.tsx +28 -0
- package/src/components/copy-button/index.tsx +54 -0
- package/src/components/core/ds-overrides.scss +105 -0
- package/src/components/core/mixins.scss +143 -0
- package/src/components/core/reset.css +131 -0
- package/src/components/core/tokens.scss +24 -0
- package/src/components/helptext/helptext.module.scss +46 -0
- package/src/components/helptext/helptext.stories.tsx +42 -0
- package/src/components/helptext/index.tsx +37 -0
- package/src/components/hstack/hstack.module.scss +5 -0
- package/src/components/hstack/hstack.stories.tsx +38 -0
- package/src/components/hstack/index.tsx +17 -0
- package/src/components/org-logo/index.tsx +27 -0
- package/src/components/org-logo/org-logo.stories.tsx +35 -0
- package/src/components/org-logo/styles.module.scss +20 -0
- package/src/components/radio-group/index.tsx +101 -0
- package/src/components/radio-group/radio-group.stories.tsx +141 -0
- package/src/components/tag-list/index.tsx +39 -0
- package/src/components/tag-list/styles.module.scss +8 -0
- package/src/components/tag-list/tag-list.stories.tsx +107 -0
- package/src/components/vstack/index.tsx +16 -0
- package/src/components/vstack/vstack.module.scss +5 -0
- package/src/components/vstack/vstack.stories.tsx +37 -0
- package/src/index.ts +11 -0
- package/src/styles/storybook.scss +34 -0
- package/src/types/scss.d.ts +9 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Popover } from '@digdir/designsystemet-react';
|
|
3
|
+
import styles from './helptext.module.scss';
|
|
4
|
+
|
|
5
|
+
export type HelpTextProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Required descriptive label for screen readers.
|
|
8
|
+
**/
|
|
9
|
+
"aria-label": string;
|
|
10
|
+
/**
|
|
11
|
+
* Placement of the Popover.
|
|
12
|
+
* @default 'right'
|
|
13
|
+
*/
|
|
14
|
+
placement?: "right" | "bottom" | "left" | "top";
|
|
15
|
+
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "color">;
|
|
16
|
+
|
|
17
|
+
export const HelpText = React.forwardRef<HTMLButtonElement, HelpTextProps>(
|
|
18
|
+
function HelpText(
|
|
19
|
+
{ placement = "right", children, ...rest },
|
|
20
|
+
ref
|
|
21
|
+
) {
|
|
22
|
+
return (
|
|
23
|
+
<Popover.TriggerContext>
|
|
24
|
+
<Popover.Trigger
|
|
25
|
+
className={styles.helptext}
|
|
26
|
+
ref={ref}
|
|
27
|
+
variant="tertiary"
|
|
28
|
+
data-color="info"
|
|
29
|
+
{...rest}
|
|
30
|
+
/>
|
|
31
|
+
<Popover placement={placement} data-color="info">
|
|
32
|
+
{children}
|
|
33
|
+
</Popover>
|
|
34
|
+
</Popover.TriggerContext>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import HStack from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof HStack> = {
|
|
6
|
+
component: HStack,
|
|
7
|
+
title: 'HStack',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof HStack>;
|
|
12
|
+
|
|
13
|
+
const Item = ({ children }: { children: React.ReactNode }) => (
|
|
14
|
+
<span
|
|
15
|
+
style={{
|
|
16
|
+
display: 'inline-block',
|
|
17
|
+
padding: '0.25rem 0.5rem',
|
|
18
|
+
background: '#EEF0F2',
|
|
19
|
+
borderRadius: 4,
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</span>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const Primary: Story = {
|
|
27
|
+
render: () => (
|
|
28
|
+
<div style={{ padding: '1rem' }}>
|
|
29
|
+
<HStack>
|
|
30
|
+
<Item>One</Item>
|
|
31
|
+
<Item>Two</Item>
|
|
32
|
+
<Item>Three</Item>
|
|
33
|
+
</HStack>
|
|
34
|
+
</div>
|
|
35
|
+
),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
|
|
4
|
+
import styles from './hstack.module.scss';
|
|
5
|
+
|
|
6
|
+
const HStack = ({ children, className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
className={cn(styles.wrapper, className)}
|
|
10
|
+
{...props}
|
|
11
|
+
>
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default HStack;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { Buildings3Icon } from '@navikt/aksel-icons';
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
|
|
6
|
+
export type OrgLogoProps = {
|
|
7
|
+
orgLogoSrc?: string | null;
|
|
8
|
+
className?: string;
|
|
9
|
+
} & React.HTMLAttributes<HTMLDivElement>;
|
|
10
|
+
|
|
11
|
+
const OrgLogo = ({ className, orgLogoSrc, ...props }: OrgLogoProps) => {
|
|
12
|
+
return (
|
|
13
|
+
<div className={cn(styles.orgLogo, className)} {...props}>
|
|
14
|
+
{orgLogoSrc ? (
|
|
15
|
+
<img
|
|
16
|
+
aria-hidden
|
|
17
|
+
src={orgLogoSrc}
|
|
18
|
+
alt=''
|
|
19
|
+
/>
|
|
20
|
+
) : (
|
|
21
|
+
<Buildings3Icon aria-hidden />
|
|
22
|
+
)}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { OrgLogo };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { OrgLogo } from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof OrgLogo> = {
|
|
6
|
+
component: OrgLogo,
|
|
7
|
+
title: 'OrgLogo',
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
argTypes: {
|
|
12
|
+
orgLogoSrc: {
|
|
13
|
+
control: 'text',
|
|
14
|
+
description: 'URL of the organization logo image',
|
|
15
|
+
},
|
|
16
|
+
className: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'Additional CSS classes',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof OrgLogo>;
|
|
25
|
+
|
|
26
|
+
export const Default: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
orgLogoSrc: 'https://orglogo.digdir.no/api/emblem/svg/991825827',
|
|
29
|
+
},
|
|
30
|
+
render: (args) => (
|
|
31
|
+
<div style={{ maxWidth: '2rem', padding: '1rem' }}>
|
|
32
|
+
<OrgLogo {...args} />
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.orgLogo {
|
|
2
|
+
display: flex;
|
|
3
|
+
width: 1em;
|
|
4
|
+
height: 1em;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
position: relative;
|
|
9
|
+
|
|
10
|
+
svg,
|
|
11
|
+
img {
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
object-fit: contain;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
svg {
|
|
18
|
+
color: var(--color-text);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Fieldset,
|
|
4
|
+
Radio,
|
|
5
|
+
ValidationMessage,
|
|
6
|
+
type UseRadioGroupProps,
|
|
7
|
+
useRadioGroup,
|
|
8
|
+
} from '@digdir/designsystemet-react';
|
|
9
|
+
|
|
10
|
+
export interface RadioGroupProps extends Omit<UseRadioGroupProps, 'value' | 'onChange'> {
|
|
11
|
+
/**
|
|
12
|
+
* The legend text for the fieldset
|
|
13
|
+
*/
|
|
14
|
+
legend?: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Optional description text for the fieldset
|
|
17
|
+
*/
|
|
18
|
+
description?: ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Array of radio options
|
|
21
|
+
*/
|
|
22
|
+
options: Array<{
|
|
23
|
+
value: string;
|
|
24
|
+
label: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
/**
|
|
29
|
+
* Controlled value - the selected radio value
|
|
30
|
+
*/
|
|
31
|
+
value?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Callback when the selected value changes
|
|
34
|
+
*/
|
|
35
|
+
onChange?: (value: string) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Optional className for the fieldset
|
|
38
|
+
*/
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* RadioGroup component that wraps multiple Radio components in a Fieldset.
|
|
44
|
+
* Handles state management, onChange, and value automatically.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* <RadioGroup
|
|
48
|
+
* legend="Hvilken iskremsmak er best?"
|
|
49
|
+
* description="Velg din favorittsmak"
|
|
50
|
+
* options={[
|
|
51
|
+
* { value: 'vanilje', label: 'Vanilje' },
|
|
52
|
+
* { value: 'jordbær', label: 'Jordbær' },
|
|
53
|
+
* { value: 'sjokolade', label: 'Sjokolade' },
|
|
54
|
+
* ]}
|
|
55
|
+
* value={selectedValue}
|
|
56
|
+
* onChange={(value) => setSelectedValue(value)}
|
|
57
|
+
* />
|
|
58
|
+
*/
|
|
59
|
+
export const RadioGroup = ({
|
|
60
|
+
legend,
|
|
61
|
+
description,
|
|
62
|
+
options,
|
|
63
|
+
value,
|
|
64
|
+
onChange,
|
|
65
|
+
error,
|
|
66
|
+
disabled,
|
|
67
|
+
readOnly,
|
|
68
|
+
required,
|
|
69
|
+
name,
|
|
70
|
+
className,
|
|
71
|
+
}: RadioGroupProps) => {
|
|
72
|
+
const { getRadioProps, validationMessageProps } = useRadioGroup({
|
|
73
|
+
value,
|
|
74
|
+
onChange: onChange ? (nextValue) => onChange(nextValue) : undefined,
|
|
75
|
+
error,
|
|
76
|
+
disabled,
|
|
77
|
+
readOnly,
|
|
78
|
+
required,
|
|
79
|
+
name,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Fieldset className={className}>
|
|
84
|
+
{legend && <Fieldset.Legend>{legend}</Fieldset.Legend>}
|
|
85
|
+
{description && <Fieldset.Description>{description}</Fieldset.Description>}
|
|
86
|
+
{options.map((option) => (
|
|
87
|
+
<Radio
|
|
88
|
+
key={option.value}
|
|
89
|
+
label={option.label}
|
|
90
|
+
description={option.description}
|
|
91
|
+
disabled={option.disabled}
|
|
92
|
+
{...getRadioProps(option.value)}
|
|
93
|
+
/>
|
|
94
|
+
))}
|
|
95
|
+
{error && <ValidationMessage {...validationMessageProps} />}
|
|
96
|
+
</Fieldset>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default RadioGroup;
|
|
101
|
+
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import RadioGroup from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof RadioGroup> = {
|
|
6
|
+
component: RadioGroup,
|
|
7
|
+
title: 'RadioGroup',
|
|
8
|
+
argTypes: {
|
|
9
|
+
legend: {
|
|
10
|
+
control: 'text',
|
|
11
|
+
description: 'The legend text for the fieldset',
|
|
12
|
+
},
|
|
13
|
+
description: {
|
|
14
|
+
control: 'text',
|
|
15
|
+
description: 'Optional description text',
|
|
16
|
+
},
|
|
17
|
+
value: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Controlled value - the selected radio value',
|
|
20
|
+
},
|
|
21
|
+
error: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'Error message to display',
|
|
24
|
+
},
|
|
25
|
+
disabled: {
|
|
26
|
+
control: 'boolean',
|
|
27
|
+
description: 'Disable all radios',
|
|
28
|
+
},
|
|
29
|
+
readOnly: {
|
|
30
|
+
control: 'boolean',
|
|
31
|
+
description: 'Make all radios read-only',
|
|
32
|
+
},
|
|
33
|
+
required: {
|
|
34
|
+
control: 'boolean',
|
|
35
|
+
description: 'Make all radios required',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default meta;
|
|
41
|
+
type Story = StoryObj<typeof RadioGroup>;
|
|
42
|
+
|
|
43
|
+
const defaultOptions = [
|
|
44
|
+
{ value: 'vanilje', label: 'Vanilje' },
|
|
45
|
+
{ value: 'jordbær', label: 'Jordbær' },
|
|
46
|
+
{ value: 'sjokolade', label: 'Sjokolade' },
|
|
47
|
+
{ value: 'spiser-ikke-is', label: 'Jeg spiser ikke iskrem' },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const Primary: Story = {
|
|
51
|
+
render: () => {
|
|
52
|
+
const [value, setValue] = useState('vanilje');
|
|
53
|
+
return (
|
|
54
|
+
<RadioGroup
|
|
55
|
+
legend="Hvilken iskremsmak er best?"
|
|
56
|
+
description="Velg din favorittsmak blant alternativene."
|
|
57
|
+
options={defaultOptions}
|
|
58
|
+
value={value}
|
|
59
|
+
onChange={setValue}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const Uncontrolled: Story = {
|
|
66
|
+
render: () => (
|
|
67
|
+
<RadioGroup
|
|
68
|
+
legend="Hvilken iskremsmak er best?"
|
|
69
|
+
description="Velg din favorittsmak blant alternativene."
|
|
70
|
+
options={defaultOptions}
|
|
71
|
+
/>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const WithError: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<RadioGroup
|
|
78
|
+
legend="Hvilken bydel bor du i?"
|
|
79
|
+
description="Trondheim er delt inn i fire bydeler"
|
|
80
|
+
options={[
|
|
81
|
+
{ value: 'ostbyen', label: 'Østbyen' },
|
|
82
|
+
{ value: 'lerkendal', label: 'Lerkendal' },
|
|
83
|
+
{ value: 'heimdal', label: 'Heimdal' },
|
|
84
|
+
{ value: 'midtbyen', label: 'Midtbyen' },
|
|
85
|
+
]}
|
|
86
|
+
error="Du må velge en bydel før du kan fortsette."
|
|
87
|
+
required
|
|
88
|
+
/>
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const Disabled: Story = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<RadioGroup
|
|
95
|
+
legend="Hvilken iskremsmak er best?"
|
|
96
|
+
options={defaultOptions}
|
|
97
|
+
disabled
|
|
98
|
+
/>
|
|
99
|
+
),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const ReadOnly: Story = {
|
|
103
|
+
render: () => (
|
|
104
|
+
<RadioGroup
|
|
105
|
+
legend="Hvilken bydel bor du i?"
|
|
106
|
+
options={[
|
|
107
|
+
{ value: 'ostbyen', label: 'Østbyen' },
|
|
108
|
+
{ value: 'lerkendal', label: 'Lerkendal' },
|
|
109
|
+
{ value: 'heimdal', label: 'Heimdal', disabled: true },
|
|
110
|
+
{ value: 'midtbyen', label: 'Midtbyen' },
|
|
111
|
+
]}
|
|
112
|
+
value="heimdal"
|
|
113
|
+
readOnly
|
|
114
|
+
/>
|
|
115
|
+
),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const WithDescriptions: Story = {
|
|
119
|
+
render: () => {
|
|
120
|
+
const [value, setValue] = useState('');
|
|
121
|
+
return (
|
|
122
|
+
<RadioGroup
|
|
123
|
+
legend="Velg pizza"
|
|
124
|
+
description="Alle pizzaene er laget på våre egne nybakte bunner"
|
|
125
|
+
options={[
|
|
126
|
+
{ value: 'ost', label: 'Bare ost' },
|
|
127
|
+
{
|
|
128
|
+
value: 'dobbeldekker',
|
|
129
|
+
label: 'Dobbeldekker',
|
|
130
|
+
description: 'Chorizo spesial med kokkens luksuskylling',
|
|
131
|
+
},
|
|
132
|
+
{ value: 'flammen', label: 'Flammen' },
|
|
133
|
+
{ value: 'snadder', label: 'Snadder' },
|
|
134
|
+
]}
|
|
135
|
+
value={value}
|
|
136
|
+
onChange={setValue}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { Tag } from '@digdir/designsystemet-react';
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
|
|
6
|
+
export type TagListProps = {
|
|
7
|
+
maxTags?: number;
|
|
8
|
+
'data-size'?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const TagList = ({ children, className, maxTags, 'data-size': dataSize, ...props }: TagListProps & React.HTMLAttributes<HTMLUListElement>) => {
|
|
12
|
+
const childArray = React.Children.toArray(children);
|
|
13
|
+
if (!childArray.length) return null;
|
|
14
|
+
return (
|
|
15
|
+
<ul
|
|
16
|
+
className={cn(styles.tagList, className)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
{childArray
|
|
20
|
+
.filter((child) => Boolean(child))
|
|
21
|
+
.slice(0, maxTags ?? childArray.length)
|
|
22
|
+
.map((child, i) => {
|
|
23
|
+
const key = React.isValidElement(child) && child.key != null
|
|
24
|
+
? child.key
|
|
25
|
+
: i;
|
|
26
|
+
return <li key={key}>{child}</li>;
|
|
27
|
+
})}
|
|
28
|
+
{maxTags && maxTags < childArray.length && (
|
|
29
|
+
<li>
|
|
30
|
+
<Tag data-size={dataSize || 'sm'}>
|
|
31
|
+
<span>+{childArray.length - maxTags}</span>
|
|
32
|
+
</Tag>
|
|
33
|
+
</li>
|
|
34
|
+
)}
|
|
35
|
+
</ul>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default TagList;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { Tag } from '@digdir/designsystemet-react';
|
|
4
|
+
import TagList from '.';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof TagList> = {
|
|
7
|
+
component: TagList,
|
|
8
|
+
title: 'TagList',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof TagList>;
|
|
13
|
+
|
|
14
|
+
export const Primary: Story = {
|
|
15
|
+
parameters: {
|
|
16
|
+
nextjs: {
|
|
17
|
+
appDirectory: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
render: () => (
|
|
21
|
+
<>
|
|
22
|
+
<div style={{ padding: '1rem' }}>
|
|
23
|
+
<TagList>
|
|
24
|
+
<Tag data-size='sm'>My tag</Tag>
|
|
25
|
+
<Tag
|
|
26
|
+
data-size='sm'
|
|
27
|
+
color='success'
|
|
28
|
+
>
|
|
29
|
+
My tag
|
|
30
|
+
</Tag>
|
|
31
|
+
<Tag
|
|
32
|
+
data-size='sm'
|
|
33
|
+
color='neutral'
|
|
34
|
+
>
|
|
35
|
+
My tag
|
|
36
|
+
</Tag>
|
|
37
|
+
<Tag
|
|
38
|
+
data-size='sm'
|
|
39
|
+
color='warning'
|
|
40
|
+
>
|
|
41
|
+
My tag
|
|
42
|
+
</Tag>
|
|
43
|
+
<Tag
|
|
44
|
+
data-size='sm'
|
|
45
|
+
color='danger'
|
|
46
|
+
>
|
|
47
|
+
My tag
|
|
48
|
+
</Tag>
|
|
49
|
+
</TagList>
|
|
50
|
+
</div>
|
|
51
|
+
</>
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const WithMaxTags: Story = {
|
|
56
|
+
parameters: {
|
|
57
|
+
nextjs: {
|
|
58
|
+
appDirectory: true,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
render: () => (
|
|
62
|
+
<>
|
|
63
|
+
<div style={{ padding: '1rem' }}>
|
|
64
|
+
<TagList maxTags={3}>
|
|
65
|
+
<Tag data-size='sm'>Tag 1</Tag>
|
|
66
|
+
<Tag
|
|
67
|
+
data-size='sm'
|
|
68
|
+
color='success'
|
|
69
|
+
>
|
|
70
|
+
Tag 2
|
|
71
|
+
</Tag>
|
|
72
|
+
<Tag
|
|
73
|
+
data-size='sm'
|
|
74
|
+
color='neutral'
|
|
75
|
+
>
|
|
76
|
+
Tag 3
|
|
77
|
+
</Tag>
|
|
78
|
+
<Tag
|
|
79
|
+
data-size='sm'
|
|
80
|
+
color='warning'
|
|
81
|
+
>
|
|
82
|
+
Tag 4
|
|
83
|
+
</Tag>
|
|
84
|
+
<Tag
|
|
85
|
+
data-size='sm'
|
|
86
|
+
color='danger'
|
|
87
|
+
>
|
|
88
|
+
Tag 5
|
|
89
|
+
</Tag>
|
|
90
|
+
<Tag data-size='sm'>Tag 6</Tag>
|
|
91
|
+
<Tag
|
|
92
|
+
data-size='sm'
|
|
93
|
+
color='success'
|
|
94
|
+
>
|
|
95
|
+
Tag 7
|
|
96
|
+
</Tag>
|
|
97
|
+
<Tag
|
|
98
|
+
data-size='sm'
|
|
99
|
+
color='neutral'
|
|
100
|
+
>
|
|
101
|
+
Tag 8
|
|
102
|
+
</Tag>
|
|
103
|
+
</TagList>
|
|
104
|
+
</div>
|
|
105
|
+
</>
|
|
106
|
+
),
|
|
107
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import styles from './vstack.module.scss';
|
|
4
|
+
|
|
5
|
+
const VStack = ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
className={styles.wrapper}
|
|
9
|
+
{...props}
|
|
10
|
+
>
|
|
11
|
+
{children}
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default VStack;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import VStack from '.';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof VStack> = {
|
|
6
|
+
component: VStack,
|
|
7
|
+
title: 'VStack',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof VStack>;
|
|
12
|
+
|
|
13
|
+
const Item = ({ children }: { children: React.ReactNode }) => (
|
|
14
|
+
<div
|
|
15
|
+
style={{
|
|
16
|
+
padding: '0.25rem 0.5rem',
|
|
17
|
+
background: '#EEF0F2',
|
|
18
|
+
borderRadius: 4,
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const Primary: Story = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<div style={{ padding: '1rem' }}>
|
|
28
|
+
<VStack>
|
|
29
|
+
<Item>Row 1</Item>
|
|
30
|
+
<Item>Row 2</Item>
|
|
31
|
+
<Item>Row 3</Item>
|
|
32
|
+
</VStack>
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './components/app-bar';
|
|
2
|
+
export * from './components/org-logo';
|
|
3
|
+
export { default as CopyButton } from './components/copy-button';
|
|
4
|
+
export { default as HStack } from './components/hstack';
|
|
5
|
+
export { default as VStack } from './components/vstack';
|
|
6
|
+
export { HelpText } from './components/helptext';
|
|
7
|
+
export { default as TagList } from './components/tag-list';
|
|
8
|
+
export { default as RadioGroup } from './components/radio-group';
|
|
9
|
+
export { default as CheckboxGroup } from './components/checkbox-group';
|
|
10
|
+
export type { RadioGroupProps } from './components/radio-group';
|
|
11
|
+
export type { CheckboxGroupProps } from './components/checkbox-group';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@layer reset, fds;
|
|
2
|
+
@import '../components/core/reset.css';
|
|
3
|
+
@import '@digdir/designsystemet-theme';
|
|
4
|
+
@import '@digdir/designsystemet-css';
|
|
5
|
+
|
|
6
|
+
html { font-size: 16px; }
|
|
7
|
+
body { font-size: 1rem; }
|
|
8
|
+
|
|
9
|
+
html, body, #storybook-root {
|
|
10
|
+
height: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Place for additional global styles */
|
|
14
|
+
body {
|
|
15
|
+
font-family:
|
|
16
|
+
system-ui,
|
|
17
|
+
-apple-system,
|
|
18
|
+
BlinkMacSystemFont,
|
|
19
|
+
'Segoe UI',
|
|
20
|
+
Roboto,
|
|
21
|
+
'Helvetica Neue',
|
|
22
|
+
Arial,
|
|
23
|
+
sans-serif;
|
|
24
|
+
background: var(--color-fdk-navy-dark);
|
|
25
|
+
color: var(--color-text);
|
|
26
|
+
|
|
27
|
+
main {
|
|
28
|
+
background: white;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
* {
|
|
33
|
+
font-family: inherit;
|
|
34
|
+
}
|