From e779447c0aadce67720839e1fc1c519bae735321 Mon Sep 17 00:00:00 2001 From: Inge Fossland Date: Wed, 30 Oct 2024 08:37:17 +0100 Subject: [PATCH] feat: add meta fields --- .storybook/StoryDecorator.tsx | 2 +- .storybook/ThemeProvider.tsx | 16 ++++ .storybook/main.ts | 4 +- .storybook/preview.tsx | 58 +++++++-------- .storybook/theme.module.css | 3 + lib/components/Avatar/avatar.module.css | 2 + lib/components/Badge/Badge.stories.ts | 32 ++++++++ lib/components/Badge/Badge.tsx | 21 +++--- lib/components/Badge/badge.module.css | 45 +++++------- lib/components/Button/ButtonBase.tsx | 2 +- lib/components/Button/button.module.css | 19 ----- lib/components/Button/buttonBase.module.css | 42 ++++++++--- lib/components/Button/comboButton.module.css | 6 +- lib/components/Layout/Layout.stories.ts | 3 - lib/components/List/ListItem.stories.tsx | 2 +- lib/components/Menu/Menu.stories.ts | 73 ++++++++++++------- lib/components/Menu/Menu.tsx | 4 +- lib/components/Menu/MenuItem.stories.ts | 17 +++-- lib/components/Menu/MenuItem.tsx | 3 +- lib/components/Menu/MenuItemBase.tsx | 12 +-- lib/components/Menu/MenuItemLabel.tsx | 2 +- lib/components/Menu/MenuItemMedia.tsx | 2 +- lib/components/Menu/MenuOption.stories.ts | 6 +- lib/components/Menu/menuItemBase.module.css | 72 ++++++++++++++++++ lib/components/Menu/menuItemLabel.module.css | 22 ++++++ lib/components/Menu/menuItemMedia.module.css | 36 +++++++++ lib/components/Meta/MetaItem.stories.ts | 2 - lib/components/Meta/MetaItem.tsx | 1 - lib/components/Meta/MetaList.stories.ts | 2 - lib/components/Meta/MetaProgress.stories.ts | 2 - lib/components/Meta/MetaTimestamp.stories.ts | 2 - lib/components/Toolbar/{index.js => index.ts} | 0 lib/components/index.ts | 9 +++ lib/css/global.css | 1 + lib/css/theme-article.css | 15 ++++ lib/css/theme.css | 2 - package.json | 4 +- pnpm-lock.yaml | 66 +++++++++++++++-- 38 files changed, 438 insertions(+), 174 deletions(-) create mode 100644 .storybook/ThemeProvider.tsx create mode 100644 .storybook/theme.module.css create mode 100644 lib/components/Badge/Badge.stories.ts create mode 100644 lib/components/Menu/menuItemBase.module.css create mode 100644 lib/components/Menu/menuItemLabel.module.css create mode 100644 lib/components/Menu/menuItemMedia.module.css rename lib/components/Toolbar/{index.js => index.ts} (100%) create mode 100644 lib/css/theme-article.css diff --git a/.storybook/StoryDecorator.tsx b/.storybook/StoryDecorator.tsx index 563cf6b..e4e1bd3 100644 --- a/.storybook/StoryDecorator.tsx +++ b/.storybook/StoryDecorator.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from "react"; +import { ReactNode } from "react"; import styles from "./storyDecorator.module.css"; interface StoryDecoratorProps { diff --git a/.storybook/ThemeProvider.tsx b/.storybook/ThemeProvider.tsx new file mode 100644 index 0000000..5d91dec --- /dev/null +++ b/.storybook/ThemeProvider.tsx @@ -0,0 +1,16 @@ +import React, { ReactNode } from "react"; +import styles from "./theme.module.css"; + +export const ThemeProvider = ({ + children, + theme, +}: { + children?: ReactNode; + theme?: Theme; +}) => { + return ( +
+ {children} +
+ ); +}; diff --git a/.storybook/main.ts b/.storybook/main.ts index 5ed61fe..97ccf74 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -6,9 +6,9 @@ const config: StorybookConfig = { "@storybook/addon-onboarding", "@storybook/addon-links", "@storybook/addon-essentials", - "@chromatic-com/storybook", - "@storybook/addon-interactions", "@storybook/addon-themes", + "@chromatic-com/storybook", + "@storybook/addon-interactions" ], framework: { name: "@storybook/react-vite", diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 66dd712..19497dc 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,39 +1,35 @@ -import React from "react"; import { withThemeByDataAttribute } from "@storybook/addon-themes"; import { Preview, StoryFn } from "@storybook/react"; import { StoryDecorator } from "./StoryDecorator"; import "../lib/css/global.css"; - /** @type { import('@storybook/react').Preview } */ const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, }, - }, - decorators: [ - (Story: StoryFn, data) => { - const { tags, globals } = data; - - return ( - - - - ); - }, - withThemeByDataAttribute({ - themes: { - global: "global", - neutral: "neutral", - company: "company", - person: "person", - }, - defaultTheme: "neutral", - }), - ], + decorators: [ + (Story: StoryFn, data) => { + const { tags, globals } = data; + return ( + + + + ); + }, + withThemeByDataAttribute({ + themes: { + global: "global", + neutral: "neutral", + company: "company", + person: "person", + }, + defaultTheme: "neutral", + }), + ], }; - -export default preview; +export default preview; \ No newline at end of file diff --git a/.storybook/theme.module.css b/.storybook/theme.module.css new file mode 100644 index 0000000..ab7ac6f --- /dev/null +++ b/.storybook/theme.module.css @@ -0,0 +1,3 @@ +.theme[data-theme] { + background: var(--theme-background-subtle); +} diff --git a/lib/components/Avatar/avatar.module.css b/lib/components/Avatar/avatar.module.css index b76d9e0..9896f0c 100644 --- a/lib/components/Avatar/avatar.module.css +++ b/lib/components/Avatar/avatar.module.css @@ -1,4 +1,6 @@ .avatar { + flex-grow: 0; + flex-shrink: 0; display: flex; justify-content: center; align-items: center; diff --git a/lib/components/Badge/Badge.stories.ts b/lib/components/Badge/Badge.stories.ts new file mode 100644 index 0000000..129e63c --- /dev/null +++ b/lib/components/Badge/Badge.stories.ts @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Badge } from './Badge'; + +const meta = { + title: 'Atoms/Badge/Badge', + component: Badge, + tags: ['autodocs'], + parameters: {}, + args: { + label: 'Badge', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Alert: Story = { + args: { + color: 'alert', + }, +}; + +export const UnreadCount: Story = { + args: { + label: '2', + color: 'alert', + }, +}; diff --git a/lib/components/Badge/Badge.tsx b/lib/components/Badge/Badge.tsx index 53cb7a5..ecf45ee 100644 --- a/lib/components/Badge/Badge.tsx +++ b/lib/components/Badge/Badge.tsx @@ -2,18 +2,21 @@ import cx from 'classnames'; import type { ReactNode } from 'react'; import styles from './badge.module.css'; +export type BadgeColor = 'subtle' | 'alert'; +export type BadgeSize = 'sm'; + export interface BadgeProps { label?: string | number; - variant?: 'neutral' | 'strong'; - size?: 'medium' | 'small'; + color?: BadgeColor; + size?: BadgeSize; + className?: string; children?: ReactNode; } -// TODO: add aria-label to the badge -export const Badge = ({ label, variant = 'neutral', size = 'medium', children }: BadgeProps) => { - const classNames = cx(styles.badge, { - [styles.strong]: variant === 'strong', - [styles.small]: size === 'small', - }); - return {label || children}; +export const Badge = ({ label, color = 'subtle', size = 'sm', className, children }: BadgeProps) => { + return ( + + {label || children} + + ); }; diff --git a/lib/components/Badge/badge.module.css b/lib/components/Badge/badge.module.css index a754ed5..4ea50a0 100644 --- a/lib/components/Badge/badge.module.css +++ b/lib/components/Badge/badge.module.css @@ -1,36 +1,27 @@ .badge { display: inline-flex; - box-sizing: border-box; - padding: 4px 8px; - justify-content: center; - min-height: 24px; - min-width: 24px; - width: auto; - height: 24px; align-items: center; - flex-shrink: 0; - background: var(--black-5, rgba(0, 0, 0, 0.05)); - color: var(--black-100, #000); + justify-content: center; + white-space: nowrap; + + font-weight: 500; + line-height: 1; + padding: 0.5em 0.5em; + + min-width: 2em; + border-radius: 2em; +} + +.badge[data-size="sm"] { font-size: 0.75rem; - font-weight: 600; - line-height: 1rem; - border-radius: 12px; } -.strong { - font-weight: 700; - background: var(--Action-Important, #e02e49); - color: #ffffff; +.badge[data-color="subtle"] { + background-color: var(--theme-surface-default); + color: var(--theme-text-subtle); } -.small { - display: flex; - width: 12px; - height: 12px; - min-height: 12px; - min-width: 12px; - padding: 4px; - justify-content: center; - align-items: center; - flex-shrink: 0; +.badge[data-color="alert"] { + background-color: #e02e49; + color: white; } diff --git a/lib/components/Button/ButtonBase.tsx b/lib/components/Button/ButtonBase.tsx index 1c480d9..266738d 100644 --- a/lib/components/Button/ButtonBase.tsx +++ b/lib/components/Button/ButtonBase.tsx @@ -5,7 +5,7 @@ import styles from './buttonBase.module.css'; export type ButtonVariant = 'solid' | 'outline' | 'dotted' | 'text'; export type ButtonSize = 'sm' | 'md' | 'lg'; -export type ButtonColor = 'primary' | 'link'; +export type ButtonColor = 'primary' | 'secondary'; export interface ButtonBaseProps extends React.HTMLAttributes { /** diff --git a/lib/components/Button/button.module.css b/lib/components/Button/button.module.css index 18d36d8..08b222f 100644 --- a/lib/components/Button/button.module.css +++ b/lib/components/Button/button.module.css @@ -1,31 +1,12 @@ .button { display: inline-flex; align-items: center; - border: 1px solid; - border-color: var(--link-base-default); -} - -.button:focus { - border-color: var(--link-base-active); -} - -.button:hover { - border-color: var(--link-base-hover); -} - -.button:active { - border-color: var(--link-base-active); } .reverse { flex-direction: row-reverse; } -.button[aria-selected="true"] { - background-color: var(--theme-background-subtle); - color: var(--theme-text-default); -} - .label { line-height: 1rem; font-weight: 600; diff --git a/lib/components/Button/buttonBase.module.css b/lib/components/Button/buttonBase.module.css index 21afee8..1b54aa4 100644 --- a/lib/components/Button/buttonBase.module.css +++ b/lib/components/Button/buttonBase.module.css @@ -1,5 +1,6 @@ .button { - border: none; + border: 1px solid; + border-color: transparent; margin: 0; padding: 0; width: auto; @@ -32,11 +33,23 @@ pointer-events: none; } +.button:focus { + border-color: var(--theme-base-active); +} + +.button:hover { + border-color: var(--theme-base-hover); +} + +.button:active { + border-color: var(--theme-base-active); +} + /* solid */ .button[data-variant="solid"] { - border-color: var(--theme-base-hover); - background-color: var(--theme-base-hover); + border-color: var(--theme-base-default); + background-color: var(--theme-base-default); color: white; } @@ -60,18 +73,23 @@ border-color: transparent; } -/* solid + link +.button[aria-selected="true"] { + background-color: var(--theme-background-subtle); + color: var(--theme-text-default); +} -.button[data-color="link"][data-variant="solid"] { - background-color: var(--link-base-default); - color: white; +/* secondary color */ + +.button[data-color="secondary"] { + color: var(--theme-text-subtle); } -.button[data-color="link"][data-variant="solid"]:hover { - background-color: var(--link-base-hover); +.button[data-color="secondary"]:hover { + border-color: var(--theme-surface-hover); } -.button[data-color="link"][data-variant="active"]:hover { - background-color: var(--link-base-active); +.button[data-color="secondary"][data-variant="solid"], +.button[data-color="secondary"][data-variant="solid"] { + border-color: var(--theme-surface-default); + background-color: var(--theme-surface-default); } - */ diff --git a/lib/components/Button/comboButton.module.css b/lib/components/Button/comboButton.module.css index c5e0a5d..d84be06 100644 --- a/lib/components/Button/comboButton.module.css +++ b/lib/components/Button/comboButton.module.css @@ -6,10 +6,12 @@ border-radius: 2px; } +/* .button[aria-selected="true"] { background-color: var(--theme-background-subtle); color: var(--theme-text-default); } + */ .label { line-height: 1rem; @@ -40,7 +42,7 @@ .label[data-size="sm"] { font-size: 0.875rem; - padding: 9px 10px; + padding: 8px 10px; } .divider[data-size="sm"] { @@ -49,7 +51,7 @@ .icon[data-size="sm"] { font-size: 1.25rem; - padding: 7px 5px; + padding: 6px 5px; } /* md 44px */ diff --git a/lib/components/Layout/Layout.stories.ts b/lib/components/Layout/Layout.stories.ts index 0df2ad4..4daa194 100644 --- a/lib/components/Layout/Layout.stories.ts +++ b/lib/components/Layout/Layout.stories.ts @@ -11,7 +11,6 @@ const menu: MenuProps = { size: 'lg', icon: 'inbox', title: 'Innboks', - badge: '4', }, { id: '2', @@ -25,14 +24,12 @@ const menu: MenuProps = { icon: 'file-checkmark', selected: true, title: 'Sendt', - badge: '2', }, { id: '4', group: 3, icon: 'bookmark', title: 'Lagrede søk', - badge: '11', }, { id: '5', diff --git a/lib/components/List/ListItem.stories.tsx b/lib/components/List/ListItem.stories.tsx index 91a4d70..8a7eade 100644 --- a/lib/components/List/ListItem.stories.tsx +++ b/lib/components/List/ListItem.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; import { Fragment, useState } from 'react'; import { MetaItem } from '../Meta'; @@ -13,6 +12,7 @@ const meta = { tags: ['autodocs'], parameters: {}, args: { + id: 'id', title: 'Title', description: 'Description', size: 'md', diff --git a/lib/components/Menu/Menu.stories.ts b/lib/components/Menu/Menu.stories.ts index dae3f5e..39a42c4 100644 --- a/lib/components/Menu/Menu.stories.ts +++ b/lib/components/Menu/Menu.stories.ts @@ -14,7 +14,6 @@ type Story = StoryObj; export const GlobalMenu: Story = { args: { - theme: 'global', groups: { settings: { defaultItemColor: 'default', @@ -38,6 +37,10 @@ export const GlobalMenu: Story = { size: 'lg', icon: 'inbox', title: 'Innboks', + badge: { + color: 'alert', + label: '4', + }, }, { id: 'access', @@ -45,6 +48,10 @@ export const GlobalMenu: Story = { size: 'lg', icon: 'bookmark', title: 'Tilganger', + badge: { + color: 'alert', + label: '2', + }, }, { id: 'access', @@ -65,7 +72,6 @@ export const GlobalMenu: Story = { export const CollapsibleGlobalMenu: Story = { args: { - theme: 'global', groups: { settings: { defaultItemColor: 'default', @@ -89,7 +95,6 @@ export const CollapsibleGlobalMenu: Story = { size: 'lg', icon: 'inbox', title: 'Innboks', - badge: '4', collapsible: true, items: [ { @@ -104,14 +109,12 @@ export const CollapsibleGlobalMenu: Story = { icon: 'file-checkmark', selected: true, title: 'Sendt', - badge: 2, }, { id: 'bookmarks', group: '3', icon: 'bookmark', title: 'Lagrede søk', - badge: 11, }, { id: 'arkiv', @@ -154,7 +157,7 @@ export const CollapsibleGlobalMenu: Story = { export const ExpandedGlobalMenu: Story = { args: { ...CollapsibleGlobalMenu.args, - items: [...(CollapsibleGlobalMenu?.args?.items ?? [])].map((item) => { + items: [...CollapsibleGlobalMenu.args.items].map((item) => { if (item.collapsible) { return { ...item, @@ -169,7 +172,6 @@ export const ExpandedGlobalMenu: Story = { export const DrilldownMenu: Story = { args: { - theme: 'global', color: 'subtle', groups: { 'level-1': { @@ -196,18 +198,18 @@ export const DrilldownMenu: Story = { expanded: true, items: [ { + id: 'c1', group: 'level-3', - name: 'c1', title: 'Kategori 1', }, { group: 'level-3', - name: 'c2', + id: 'c2', title: 'Kategori 2', }, { group: 'level-3', - name: 'c3', + id: 'c3', title: 'Kategori 3', }, ], @@ -220,9 +222,7 @@ export const DrilldownMenu: Story = { export const InboxMenu: Story = { args: { - theme: 'global', groups: {}, - items: [ { id: 'innboks', @@ -231,13 +231,16 @@ export const InboxMenu: Story = { icon: 'inbox', title: 'Innboks', color: 'strong', - badge: 4, + badge: { color: 'alert', label: '4' }, }, { id: 'utkast', group: '2', icon: 'doc-pencil', title: 'Utkast', + badge: { + label: '3', + }, }, { id: 'sendt', @@ -245,20 +248,27 @@ export const InboxMenu: Story = { icon: 'file-checkmark', selected: true, title: 'Sendt', - badge: 2, + badge: { + label: '2', + }, }, { id: 'lagret', group: '3', icon: 'bookmark', title: 'Lagrede søk', - badge: 11, + badge: { + label: '5', + }, }, { id: 'arkivert', group: '4', icon: 'archive', title: 'Arkivert', + badge: { + label: '100+', + }, }, { id: 'papirkurv', @@ -266,16 +276,18 @@ export const InboxMenu: Story = { disabled: true, icon: 'trash', title: 'Papirkurv', + badge: { + label: '45', + }, }, ], - color: 'subtle', + defaultItemColor: 'subtle', }, }; export const InboxMenuWithShortcuts = { args: { - theme: 'global', groups: { ...InboxMenu.args?.groups, shortcuts: { @@ -284,7 +296,7 @@ export const InboxMenuWithShortcuts = { }, }, items: [ - ...(InboxMenu.args?.items ?? []), + ...(InboxMenu?.args?.items ?? []), { id: 'users', group: 'shortcuts', @@ -303,7 +315,6 @@ export const InboxMenuWithShortcuts = { export const PersonMenu: Story = { args: { - theme: 'global', groups: {}, items: [ { @@ -352,7 +363,6 @@ export const PersonMenu: Story = { export const CompanyMenu: Story = { args: { - theme: 'global', groups: {}, items: [ { @@ -395,12 +405,12 @@ export const CompanyMenu: Story = { export const AccountMenu: Story = { args: { - theme: 'global', groups: { a1: { title: 'Deg selv, favoritter og grupper', }, b1: { + id: 'companies', title: 'Andre kontoer', }, }, @@ -413,7 +423,9 @@ export const AccountMenu: Story = { name: 'Dolly Duck', }, title: 'Dolly Duck', - badge: '15', + badge: { + label: '15', + }, }, { id: '2', @@ -423,7 +435,9 @@ export const AccountMenu: Story = { name: 'Bergen Bar', }, title: 'Bergen Bar', - badge: 21, + badge: { + label: '21', + }, }, { id: '3', @@ -433,7 +447,9 @@ export const AccountMenu: Story = { name: 'Sportsklubben Brann', }, title: 'Sportsklubben Brann', - badge: '4', + badge: { + label: '4', + }, }, { id: '4', @@ -450,6 +466,9 @@ export const AccountMenu: Story = { ], }, title: 'Alle virksomheter', + badge: { + label: '45', + }, }, { id: '5', @@ -468,6 +487,9 @@ export const AccountMenu: Story = { name: 'Haralds gym', }, title: 'Haralds gym', + badge: { + label: '2', + }, }, { id: '7', @@ -487,9 +509,6 @@ export const AccountMenuWithSearch: Story = { ...AccountMenu.args, search: { placeholder: 'Søk i kontoer', - name: 'search', - value: '', - onChange: () => {}, }, }, }; diff --git a/lib/components/Menu/Menu.tsx b/lib/components/Menu/Menu.tsx index cc4c4b5..092c75e 100644 --- a/lib/components/Menu/Menu.tsx +++ b/lib/components/Menu/Menu.tsx @@ -8,7 +8,7 @@ import type { MenuItemColor, MenuItemSize } from './MenuItemBase'; import { MenuItem, type MenuItemProps } from './MenuItem'; import styles from './menu.module.css'; -export type MenuTheme = 'global' | 'neutral' | 'company' | 'person'; +export type MenuTheme = 'inherit' | 'global' | 'neutral' | 'company' | 'person'; interface MenuItemsGroupProps { title?: string; @@ -107,7 +107,7 @@ export const MenuItems = ({ }; export const Menu = ({ - theme, + theme = 'inherit', defaultItemColor = 'subtle', defaultItemSize = 'sm', groups, diff --git a/lib/components/Menu/MenuItem.stories.ts b/lib/components/Menu/MenuItem.stories.ts index 6c6ba7d..560b7ba 100644 --- a/lib/components/Menu/MenuItem.stories.ts +++ b/lib/components/Menu/MenuItem.stories.ts @@ -6,7 +6,9 @@ const meta = { component: MenuItem, tags: ['autodocs'], parameters: {}, - args: {}, + args: { + id: 'inbox', + }, } satisfies Meta; export default meta; @@ -23,7 +25,9 @@ export const DefaultBadge: Story = { args: { icon: 'inbox', title: 'Innboks', - badge: 4, + badge: { + label: '4', + }, }, }; @@ -36,13 +40,16 @@ export const Large: Story = { }, }; -export const LargeBadge: Story = { +export const AlertBadge: Story = { args: { size: 'lg', icon: 'inbox', title: 'Innboks', - badge: 4, color: 'strong', + badge: { + color: 'alert', + label: '4', + }, }, }; @@ -81,8 +88,8 @@ export const PersonGroup: Story = { export const Company: Story = { args: { avatar: { - name: 'Sportsklubben Brann', type: 'company', + name: 'Sportsklubben Brann', }, title: 'Sportsklubben Brann', }, diff --git a/lib/components/Menu/MenuItem.tsx b/lib/components/Menu/MenuItem.tsx index 4f68920..76f76a2 100644 --- a/lib/components/Menu/MenuItem.tsx +++ b/lib/components/Menu/MenuItem.tsx @@ -1,5 +1,6 @@ import type { ElementType, ReactNode } from 'react'; import type { AvatarGroupProps, AvatarProps } from '../Avatar'; +import type { BadgeProps } from '../Badge'; import type { IconName } from '../Icon'; import { MenuItemBase, type MenuItemColor, type MenuItemSize } from './MenuItemBase'; import { MenuItemLabel } from './MenuItemLabel'; @@ -22,7 +23,7 @@ export interface MenuItemProps { title?: string; description?: string; label?: string; - badge?: string; + badge?: BadgeProps; icon?: IconName; avatar?: AvatarProps; avatarGroup?: AvatarGroupProps; diff --git a/lib/components/Menu/MenuItemBase.tsx b/lib/components/Menu/MenuItemBase.tsx index 1c76fe3..e82a4ee 100644 --- a/lib/components/Menu/MenuItemBase.tsx +++ b/lib/components/Menu/MenuItemBase.tsx @@ -1,8 +1,8 @@ import cx from 'classnames'; import type { ElementType, ReactNode } from 'react'; -import { Badge } from '../Badge'; +import { Badge, type BadgeProps } from '../Badge'; import { Icon, type IconName } from '../Icon'; -import styles from './menuItem.module.css'; +import styles from './menuItemBase.module.css'; export type MenuItemColor = 'default' | 'subtle' | 'strong' | 'company' | 'person'; export type MenuItemSize = 'sm' | 'md' | 'lg'; @@ -13,7 +13,7 @@ export interface MenuItemBaseProps { children?: ReactNode; size?: MenuItemSize; linkIcon?: IconName; - badge?: string | number | undefined; + badge?: BadgeProps; collapsible?: boolean; expanded?: boolean; selected?: boolean; @@ -52,10 +52,10 @@ export const MenuItemBase = ({ >
{children} - {badge && {badge}} + {badge && }
-
- {applicableIcon && } +
+ {applicableIcon && }
); diff --git a/lib/components/Menu/MenuItemLabel.tsx b/lib/components/Menu/MenuItemLabel.tsx index 7a7655f..3c0ee58 100644 --- a/lib/components/Menu/MenuItemLabel.tsx +++ b/lib/components/Menu/MenuItemLabel.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react'; import type { MenuItemSize } from './MenuItemBase'; -import styles from './menuItem.module.css'; +import styles from './menuItemLabel.module.css'; export interface MenuItemLabelProps { size?: MenuItemSize; diff --git a/lib/components/Menu/MenuItemMedia.tsx b/lib/components/Menu/MenuItemMedia.tsx index da4db67..4da99d9 100644 --- a/lib/components/Menu/MenuItemMedia.tsx +++ b/lib/components/Menu/MenuItemMedia.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import { Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, type AvatarSize } from '../Avatar'; import { Icon, type IconName } from '../Icon'; import type { MenuItemColor, MenuItemSize } from './MenuItemBase'; -import styles from './menuItem.module.css'; +import styles from './menuItemMedia.module.css'; interface MenuItemMediaProps { color?: MenuItemColor; diff --git a/lib/components/Menu/MenuOption.stories.ts b/lib/components/Menu/MenuOption.stories.ts index 4ffb0e4..c23106a 100644 --- a/lib/components/Menu/MenuOption.stories.ts +++ b/lib/components/Menu/MenuOption.stories.ts @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; - import { MenuOption } from './MenuOption'; const meta = { @@ -7,7 +6,10 @@ const meta = { component: MenuOption, tags: ['autodocs'], parameters: {}, - args: {}, + args: { + value: '', + label: 'Label', + }, } satisfies Meta; export default meta; diff --git a/lib/components/Menu/menuItemBase.module.css b/lib/components/Menu/menuItemBase.module.css new file mode 100644 index 0000000..bb81858 --- /dev/null +++ b/lib/components/Menu/menuItemBase.module.css @@ -0,0 +1,72 @@ +.item { + background-color: transparent; + display: flex; + align-items: center; + column-gap: 4px; + border: 0; + user-select: none; + cursor: pointer; + margin: 0.5rem 0; +} + +.item[aria-disabled="true"] { + opacity: 0.5; + pointer-events: none; +} + +/* size */ + +.item[data-size="sm"] { + min-height: 44px; +} + +/* content */ + +.content { + display: flex; + width: 100%; + align-items: center; + column-gap: 6px; + padding: 6px; +} + +.action { + display: flex; + justify-content: center; + align-items: center; + padding: 10px; +} + +.actionIcon { + font-size: 1.5rem; +} + +/* colors */ + +.item:hover { + background-color: var(--theme-background-default); +} + +.item[aria-selected="true"] { + background-color: var(--theme-background-default); +} + +/* company */ + +.item[data-color="company"]:hover { + background-color: var(--company-background-subtle); +} + +.item[data-color="company"][aria-selected="true"] { + background-color: var(--company-surface-default); +} + +/* person */ + +.item[data-color="person"]:hover { + background-color: var(--person-background-subtle); +} + +.item[data-color="person"][aria-selected="true"] { + background-color: var(--person-surface-default); +} diff --git a/lib/components/Menu/menuItemLabel.module.css b/lib/components/Menu/menuItemLabel.module.css new file mode 100644 index 0000000..c0d2bfb --- /dev/null +++ b/lib/components/Menu/menuItemLabel.module.css @@ -0,0 +1,22 @@ +.label { + display: flex; + flex-direction: column; + padding: 0 0.25rem; +} + +.title[data-size="lg"] { + font-size: 1.125rem; + line-height: 1.25; + font-weight: 500; +} + +.title[data-size="sm"] { + font-size: 1rem; + line-height: 1.25; + font-weight: 400; +} + +.description { + font-size: 14px; + color: var(--theme-text-subtle); +} diff --git a/lib/components/Menu/menuItemMedia.module.css b/lib/components/Menu/menuItemMedia.module.css new file mode 100644 index 0000000..a93a441 --- /dev/null +++ b/lib/components/Menu/menuItemMedia.module.css @@ -0,0 +1,36 @@ +.media { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 5%; + width: 2rem; + height: 2rem; +} + +.media[data-size="lg"] { + width: 44px; + height: 44px; +} + +.icon { + font-size: 1.5rem; +} + +.media[data-color="subtle"] { + background-color: var(--theme-background-default); + color: var(--theme-text-default); +} + +.media[data-color="strong"] { + background-color: var(--theme-base-default); + color: var(--theme-background-default); +} + +.media[data-color="company"] { + background-color: var(--company-surface-default); +} + +.media[data-color="person"] { + background-color: var(--person-surface-default); +} diff --git a/lib/components/Meta/MetaItem.stories.ts b/lib/components/Meta/MetaItem.stories.ts index 7c10b02..227e065 100644 --- a/lib/components/Meta/MetaItem.stories.ts +++ b/lib/components/Meta/MetaItem.stories.ts @@ -1,6 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - import { MetaItem } from './MetaItem'; const meta = { diff --git a/lib/components/Meta/MetaItem.tsx b/lib/components/Meta/MetaItem.tsx index c28aecb..2f8cb2f 100644 --- a/lib/components/Meta/MetaItem.tsx +++ b/lib/components/Meta/MetaItem.tsx @@ -1,5 +1,4 @@ import type { ReactNode } from 'react'; - import type { IconName } from '../Icon'; import { MetaItemBase, type MetaItemSize, type MetaItemVariant } from './MetaItemBase'; import { MetaItemLabel } from './MetaItemLabel'; diff --git a/lib/components/Meta/MetaList.stories.ts b/lib/components/Meta/MetaList.stories.ts index af8b2c3..82af13c 100644 --- a/lib/components/Meta/MetaList.stories.ts +++ b/lib/components/Meta/MetaList.stories.ts @@ -1,6 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - import { MetaList } from './MetaList'; const meta = { diff --git a/lib/components/Meta/MetaProgress.stories.ts b/lib/components/Meta/MetaProgress.stories.ts index 2ab58da..c49e70d 100644 --- a/lib/components/Meta/MetaProgress.stories.ts +++ b/lib/components/Meta/MetaProgress.stories.ts @@ -1,6 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - import { MetaProgress } from './MetaProgress'; const meta = { diff --git a/lib/components/Meta/MetaTimestamp.stories.ts b/lib/components/Meta/MetaTimestamp.stories.ts index b1bd1cd..89f9408 100644 --- a/lib/components/Meta/MetaTimestamp.stories.ts +++ b/lib/components/Meta/MetaTimestamp.stories.ts @@ -1,6 +1,4 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - import { MetaTimestamp } from './MetaTimestamp'; const meta = { diff --git a/lib/components/Toolbar/index.js b/lib/components/Toolbar/index.ts similarity index 100% rename from lib/components/Toolbar/index.js rename to lib/components/Toolbar/index.ts diff --git a/lib/components/index.ts b/lib/components/index.ts index 27700fe..9b9a72f 100644 --- a/lib/components/index.ts +++ b/lib/components/index.ts @@ -1 +1,10 @@ export * from './Avatar'; +export * from './Button'; +export * from './Badge'; +export * from './Header'; +export * from './Icon'; +export * from './Layout'; +export * from './List'; +export * from './Menu'; +export * from './Meta'; +export * from './Toolbar'; diff --git a/lib/css/global.css b/lib/css/global.css index 532bd09..ab0d836 100644 --- a/lib/css/global.css +++ b/lib/css/global.css @@ -2,6 +2,7 @@ @import "./colors.css"; @import "./shadows.css"; @import "./theme-global.css"; +@import "./theme-article.css"; @import "./theme-neutral.css"; @import "./theme-company.css"; @import "./theme-person.css"; diff --git a/lib/css/theme-article.css b/lib/css/theme-article.css new file mode 100644 index 0000000..c2e6d37 --- /dev/null +++ b/lib/css/theme-article.css @@ -0,0 +1,15 @@ +[data-theme="article"] { + --theme-background-default: var(--neutral-background-subtle); + --theme-background-subtle: var(--neutral-background-default); + --theme-base-active: var(--link-base-active); + --theme-base-default: var(--link-base-default); + --theme-base-hover: var(--link-base-hover); + --theme-border-default: var(--neutral-border-default); + --theme-border-strong: var(--neutral-border-strong); + --theme-border-subtle: var(--neutral-border-subtle); + --theme-surface-active: var(--neutral-surface-active); + --theme-surface-default: var(--neutral-surface-default); + --theme-surface-hover: var(--neutral-surface-hover); + --theme-text-default: var(--neutral-text-default); + --theme-text-subtle: var(--neutral-text-subtle); +} diff --git a/lib/css/theme.css b/lib/css/theme.css index 5e6c73c..c81a980 100644 --- a/lib/css/theme.css +++ b/lib/css/theme.css @@ -13,12 +13,10 @@ html { --theme-text-default: var(--global-text-default); --theme-text-subtle: var(--global-text-subtle); } - html[data-theme] { background: var(--theme-background-subtle); color: var(--neutral-text-default); } - body { background-color: inherit; } diff --git a/package.json b/package.json index c973879..c23741b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@storybook/addon-interactions": "^8.3.5", "@storybook/addon-links": "^8.3.5", "@storybook/addon-onboarding": "^8.3.5", - "@storybook/addon-themes": "^8.4.1", + "@storybook/addon-themes": "^8.3.5", "@storybook/blocks": "^8.3.5", "@storybook/react": "^8.3.5", "@storybook/react-vite": "^8.3.5", @@ -40,7 +40,7 @@ "lint-staged": "^15.2.10", "prop-types": "^15.8.1", "storybook": "^8.3.5", - "storybook-css-modules": "^1.0.8", + "storybook-addon-theme-provider": "^0.2.6", "typescript": "^5.6.3", "vite": "^5.4.9" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7be5a5..9b2123b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,7 @@ importers: specifier: ^8.3.5 version: 8.3.6(react@18.3.1)(storybook@8.3.6) '@storybook/addon-themes': - specifier: ^8.4.1 + specifier: ^8.3.5 version: 8.4.1(storybook@8.3.6) '@storybook/blocks': specifier: ^8.3.5 @@ -69,9 +69,9 @@ importers: storybook: specifier: ^8.3.5 version: 8.3.6 - storybook-css-modules: - specifier: ^1.0.8 - version: 1.0.8 + storybook-addon-theme-provider: + specifier: ^0.2.6 + version: 0.2.6(@storybook/blocks@8.3.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.3.6))(@storybook/components@8.3.6(storybook@8.3.6))(@storybook/manager-api@8.3.6(storybook@8.3.6))(@storybook/preview-api@8.3.6(storybook@8.3.6))(@storybook/theming@8.3.6(storybook@8.3.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -533,6 +533,13 @@ packages: '@navikt/aksel-icons@7.4.1': resolution: {integrity: sha512-s1TcZIXkpfHM2e662WNCW1F1VtjvDv8aZyz5ahn6bcVRphdnrGYUcO+O4Lg5avgLGRzvLGeDxFbU9yFc+R1RDQ==} + '@phosphor-icons/react@2.1.7': + resolution: {integrity: sha512-g2e2eVAn1XG2a+LI09QU3IORLhnFNAFkNbo2iwbX6NOKSLOwvEMmTa7CgOzEbgNWR47z8i8kwjdvYZ5fkGx1mQ==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + '@rollup/pluginutils@5.1.3': resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} engines: {node: '>=14.0.0'} @@ -2018,9 +2025,37 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - storybook-css-modules@1.0.8: - resolution: {integrity: sha512-anITwllH6nLw0quPElVBLRrE8QDbcRv0Dgl8sKLOc4uiqw+g1GE2l21Stjx3Wyv2O6ZKJScbyOpOuuz3SmeaOQ==} - engines: {node: '>=14'} + storybook-addon-theme-provider@0.2.6: + resolution: {integrity: sha512-opkC7MwMrjpcOd9NJl6MpwfFM8kwwMLUoPK0aLMfBUJycyHrpJ4krRSOfB/BHY6bkdJjJQGpFBQHOSpb8ij3Gg==} + peerDependencies: + '@storybook/blocks': '>=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' + '@storybook/types': '>=7.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + peerDependenciesMeta: + '@storybook/blocks': + optional: true + '@storybook/components': + optional: true + '@storybook/core-events': + optional: true + '@storybook/manager-api': + optional: true + '@storybook/preview-api': + optional: true + '@storybook/theming': + optional: true + '@storybook/types': + optional: true + react: + optional: true + react-dom: + optional: true storybook@8.3.6: resolution: {integrity: sha512-9GVbtej6ZzPRUM7KRQ7848506FfHrUiJGqPuIQdoSJd09EmuEoLjmLAgEOmrHBQKgGYMaM7Vh9GsTLim6vwZTQ==} @@ -2574,6 +2609,11 @@ snapshots: '@navikt/aksel-icons@7.4.1': {} + '@phosphor-icons/react@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@rollup/pluginutils@5.1.3(rollup@4.24.3)': dependencies: '@types/estree': 1.0.6 @@ -4164,7 +4204,17 @@ snapshots: statuses@2.0.1: {} - storybook-css-modules@1.0.8: {} + storybook-addon-theme-provider@0.2.6(@storybook/blocks@8.3.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.3.6))(@storybook/components@8.3.6(storybook@8.3.6))(@storybook/manager-api@8.3.6(storybook@8.3.6))(@storybook/preview-api@8.3.6(storybook@8.3.6))(@storybook/theming@8.3.6(storybook@8.3.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@phosphor-icons/react': 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@storybook/blocks': 8.3.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.3.6) + '@storybook/components': 8.3.6(storybook@8.3.6) + '@storybook/manager-api': 8.3.6(storybook@8.3.6) + '@storybook/preview-api': 8.3.6(storybook@8.3.6) + '@storybook/theming': 8.3.6(storybook@8.3.6) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) storybook@8.3.6: dependencies: