Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/metal-pigs-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@navikt/ds-react": minor
"@navikt/ds-css": minor
---

InlineMessage: :tada: New component! Replaces `<Alert inline />` variant as a standalone component.
5 changes: 5 additions & 0 deletions @navikt/core/css/config/_mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ const StyleMappings = {
dependencies: ["popover.css"],
},
{ component: "Ingress", main: typoCss },
{
component: "InlineMessage",
main: "inline-message.css",
dependencies: [typoCss],
},
{
component: "InternalHeader",
main: "internalheader.css",
Expand Down
1 change: 1 addition & 0 deletions @navikt/core/css/darkside/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
@import "./expansioncard.darkside.css" layer(aksel.components);
@import "./guide-panel.darkside.css" layer(aksel.components);
@import "./help-text.darkside.css" layer(aksel.components);
@import "./inline-message.darkside.css" layer(aksel.components);
@import "./internalheader.darkside.css" layer(aksel.components);
@import "./link.darkside.css" layer(aksel.components);
@import "./link-panel.darkside.css" layer(aksel.components);
Expand Down
15 changes: 15 additions & 0 deletions @navikt/core/css/darkside/inline-message.darkside.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.aksel-inline-message {
display: flex;
gap: var(--ax-space-4);
color: var(--ax-text-default);
}

.aksel-inline-message__icon {
color: var(--ax-text-subtle);
font-size: 1.5rem;
flex: 0 0 auto;

.aksel-inline-message[data-size="small"] & {
font-size: 1.25rem;
}
}
1 change: 1 addition & 0 deletions @navikt/core/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@import "guide-panel.css";
@import "form/index.css";
@import "help-text.css";
@import "inline-message.css";
@import "internalheader.css";
@import "link.css";
@import "link-anchor.css";
Expand Down
45 changes: 45 additions & 0 deletions @navikt/core/css/inline-message.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
.navds-inline-message {
display: flex;
gap: var(--a-spacing-1);
}

.navds-inline-message[data-color="info"] {
color: var(--a-lightblue-900);

& .navds-inline-message__icon {
color: var(--a-icon-info);
}
}

.navds-inline-message[data-color="success"] {
color: var(--a-green-900);

& .navds-inline-message__icon {
color: var(--a-icon-success);
}
}

.navds-inline-message[data-color="danger"] {
color: var(--a-red-900);

& .navds-inline-message__icon {
color: var(--a-icon-danger);
}
}

.navds-inline-message[data-color="warning"] {
color: var(--a-orange-900);

& .navds-inline-message__icon {
color: var(--a-icon-warning);
}
}

.navds-inline-message__icon {
font-size: 1.5rem;
flex: 0 0 auto;

.navds-inline-message[data-size="small"] & {
font-size: 1.25rem;
}
}
10 changes: 10 additions & 0 deletions @navikt/core/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,16 @@
"default": "./cjs/form/form-progress/index.js"
}
},
"./InlineMessage": {
"import": {
"types": "./esm/inline-message/index.d.ts",
"default": "./esm/inline-message/index.js"
},
"require": {
"types": "./cjs/inline-message/index.d.ts",
"default": "./cjs/inline-message/index.js"
}
},
"./package.json": "./package.json",
"./Theme": {
"import": {
Expand Down
1 change: 1 addition & 0 deletions @navikt/core/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export {
type LinkCardIconProps,
type LinkCardImageProps,
} from "./link-card";
export { InlineMessage, type InlineMessageProps } from "./inline-message";

/**
* Theming
Expand Down
119 changes: 119 additions & 0 deletions @navikt/core/react/src/inline-message/InlineMessage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import React from "react";
import { VStack } from "../layout/stack";
import { Link } from "../link";
import { renderStoriesForChromatic } from "../util/renderStoriesForChromatic";
import { InlineMessage } from "./index";

const meta: Meta<typeof InlineMessage> = {
title: "ds-react/InlineMessage",
component: InlineMessage,
parameters: {
chromatic: { disable: true },
},
};

export default meta;

type Story = StoryObj<typeof InlineMessage>;

const variants = ["info", "success", "warning", "error"] as const;

export const Default: Story = {
render: (props) => {
return (
<InlineMessage variant={props.variant ?? "warning"}>
{props.children ?? "InlineMessage content"}
</InlineMessage>
);
},

args: {
children: "Id elit esse enim reprehenderit enim nisi veniam nostrud.",
variant: "warning",
},
argTypes: {
variant: {
control: { type: "select" },
options: variants,
},
},
};

export const SizeSmall: Story = {
render: () => {
return (
<InlineMessage variant="warning" size="small">
<DemoContent />
</InlineMessage>
);
},
};

export const Compositions: Story = {
render: () => {
return (
<VStack gap="space-16">
{variants.map((variant) => (
<InlineMessage variant={variant} key={variant}>
<DemoContent />
</InlineMessage>
))}
</VStack>
);
},
};

export const WrappingTitle: Story = {
render: () => {
return (
<InlineMessage variant="warning">
<DemoContent />
</InlineMessage>
);
},
};

export const AsLink: Story = {
render: () => {
return (
<InlineMessage variant="warning" as={Link} href="#">
This is a link inside the InlineMessage
</InlineMessage>
);
},
};

export const Chromatic = renderStoriesForChromatic({
Default,
Compositions,
WrappingTitle,
SizeSmall,
});

export const ChromaticLight = renderStoriesForChromatic({
Default,
Compositions,
WrappingTitle,
SizeSmall,
});
ChromaticLight.globals = { theme: "light", mode: "darkside" };

export const ChromaticDark = renderStoriesForChromatic({
Default,
Compositions,
WrappingTitle,
SizeSmall,
});
ChromaticDark.globals = { theme: "dark", mode: "darkside" };

function DemoContent() {
return (
<span>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Iure unde,
repudiandae, deleniti exercitationem quod aut veniam sint officiis
necessitatibus nulla nostrum voluptatem <Link href="#">Test</Link>{" "}
facilis! Commodi, nobis tempora quibusdam temporibus nulla quam.
</span>
);
}
40 changes: 40 additions & 0 deletions @navikt/core/react/src/inline-message/icon/InlineMessageIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import {
CheckmarkCircleFillIcon,
ExclamationmarkTriangleFillIcon,
InformationSquareFillIcon,
XMarkOctagonFillIcon,
} from "@navikt/aksel-icons";
import { useRenameCSS } from "../../theme/Theme";
import { useI18n } from "../../util/i18n/i18n.hooks";

const VARIANT_ICONS = {
info: InformationSquareFillIcon,
success: CheckmarkCircleFillIcon,
warning: ExclamationmarkTriangleFillIcon,
error: XMarkOctagonFillIcon,
} as const;

function InlineMessageIcon({
variant,
}: {
variant: "info" | "success" | "warning" | "error";
}) {
const translate = useI18n("Alert");
const { cn } = useRenameCSS();

if (!(variant in VARIANT_ICONS)) {
return null;
}

const Icon = VARIANT_ICONS[variant];

return (
<Icon
title={translate(variant)}
className={cn("navds-inline-message__icon")}
/>
);
}

export { InlineMessageIcon };
3 changes: 3 additions & 0 deletions @navikt/core/react/src/inline-message/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use client";
export { InlineMessage } from "./root/InlineMessage";
export type { InlineMessageProps } from "./root/InlineMessage";
74 changes: 74 additions & 0 deletions @navikt/core/react/src/inline-message/root/InlineMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, { forwardRef } from "react";
import { useRenameCSS, useThemeInternal } from "../../theme/Theme";
import { BodyShort } from "../../typography";
import type { OverridableComponent } from "../../util";
import { InlineMessageIcon } from "../icon/InlineMessageIcon";

interface InlineMessageProps extends React.HTMLAttributes<HTMLDivElement> {
/**
* InlineMessage variant.
*/
variant: "info" | "success" | "warning" | "error";
/**
* InlineMessage size.
* @default "medium"
*/
size?: "medium" | "small";
}

/**
* InlineMessage is used to display important messages together with other content.
* @see [πŸ“ Documentation](https://aksel.nav.no/komponenter/core/inline-message)
* @see 🏷️ {@link InlineMessageProps}
* @see [πŸ€– OverridableComponent](https://aksel.nav.no/grunnleggende/kode/overridablecomponent) support
* @example
* ```jsx
* <InlineMessage variant="error">
* Inline Errormessage
* </InlineMessage>
* ```
*
* @example
* As a link
* ```jsx
* <InlineMessage variant="error" as={Link} href="#">
* Inline Errormessage
* </InlineMessage>
* ```
*/
const InlineMessage: OverridableComponent<InlineMessageProps, HTMLDivElement> =
forwardRef(
(
{
as: Component = "div",
children,
className,
variant,
size = "medium",
...restProps
}: InlineMessageProps & { as?: React.ElementType },
forwardedRef,
) => {
const { cn } = useRenameCSS();
const themeContext = useThemeInternal(false);

return (
<BodyShort
ref={forwardedRef}
className={cn("navds-inline-message", className)}
data-color={variant === "error" ? "danger" : variant}
{...restProps}
size={size}
as={Component}
data-variant={variant}
data-size={size}
>
<InlineMessageIcon variant={variant} />
<span data-color={themeContext?.color}>{children}</span>
</BodyShort>
);
},
);

export { InlineMessage };
export type { InlineMessageProps };