diff --git a/.changeset/tame-rats-mix.md b/.changeset/tame-rats-mix.md
new file mode 100644
index 0000000000..827c655035
--- /dev/null
+++ b/.changeset/tame-rats-mix.md
@@ -0,0 +1,9 @@
+---
+"@digdir/designsystemet-css": patch
+"@digdir/designsystemet-react": patch
+---
+
+HelpText:
+- Use Popover API
+- Remove `portal` prop
+- Render icon with pseudo element and require aria-label
diff --git a/packages/css/button.css b/packages/css/button.css
index bbd72ae8c6..6effbd8f56 100644
--- a/packages/css/button.css
+++ b/packages/css/button.css
@@ -142,8 +142,8 @@
*/
@media (hover: hover) and (pointer: fine) {
- /* Only use hover for non-touch devices to prevent sticky-hovering */
- &:not(:disabled, [aria-disabled='true'], [aria-busy='true']):hover {
+ /* Only use hover for non-touch devices to prevent sticky-hovering, using :where to prevent adding specificity */
+ &:where(:not(:disabled, [aria-disabled='true'], [aria-busy='true'])):hover {
background: var(--dsc-button-background--hover);
}
}
@@ -157,7 +157,8 @@
opacity: var(--ds-disabled-opacity);
}
- &:not(:disabled, [aria-disabled='true'], [aria-busy='true']):active {
+ /* Using :where to prevent adding specificity */
+ &:where(:not(:disabled, [aria-disabled='true'], [aria-busy='true'])):active {
background-color: var(--dsc-button-background--active);
}
}
diff --git a/packages/css/helptext.css b/packages/css/helptext.css
index 0d3deaa9ff..604a3fe138 100644
--- a/packages/css/helptext.css
+++ b/packages/css/helptext.css
@@ -1,60 +1,48 @@
-.ds-helptext__button {
- --dsc-helptext-color: var(--ds-color-neutral-text-default);
- --dsc-helptext-icon-color: var(--ds-color-accent-base-default);
- --dsc-helptext-icon-width: var(--ds-sizing-7);
- --dsc-helptext-icon-height: var(--ds-sizing-7);
+.ds-helptext {
+ --dsc-helptext-icon-size: 65%;
+ --dsc-helptext-icon-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 8 14'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4 11a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM4 0c2.2 0 4 1.66 4 3.7 0 .98-.42 1.9-1.17 2.6l-.6.54-.29.29c-.48.46-.87.93-.94 1.41V9.5H3v-.81c0-1.26.84-2.22 1.68-3L5.42 5C5.8 4.65 6 4.2 6 3.7 6 2.66 5.1 1.83 4 1.83c-1.06 0-1.92.75-2 1.7v.15H0C0 1.66 1.8 0 4 0Z' clip-rule='evenodd'/%3E%3C/svg%3E");
+ --dsc-helptext-size: var(--ds-sizing-7);
- background-color: transparent;
- border-radius: 50px;
- padding: 0 !important;
- cursor: pointer;
- display: flex;
- border: none;
- min-width: 0;
- min-height: 0;
-}
+ @composes ds-focus from '../css/utilities.css';
-@media print {
- .ds-helptext__button {
- display: none;
+ border-radius: var(--ds-border-radius-full);
+ border: 2px solid;
+ height: var(--dsc-helptext-size);
+ min-height: 0;
+ min-width: 0;
+ padding: 0;
+ position: relative;
+ width: var(--dsc-helptext-size);
+
+ &::before {
+ content: '';
+ border-radius: inherit;
+ background: currentcolor;
+ mask-composite: exclude;
+ mask-image: var(--dsc-helptext-icon-url);
+ mask-position: center;
+ mask-repeat: no-repeat;
+ mask-size:
+ var(--dsc-helptext-icon-size) var(--dsc-helptext-icon-size),
+ cover;
+ scale: 1.1; /* Hide tiny half pixel rendeing bug */
+ width: 100%;
+ height: 100%;
}
-}
-
-.ds-helptext__icon--filled {
- display: none;
-}
-.ds-helptext__icon {
- color: var(--dsc-helptext-icon-color);
- width: var(--dsc-helptext-icon-width);
- height: var(--dsc-helptext-icon-height);
-}
-
-.ds-helptext__button:where(:hover, :focus, [data-state^='open']) > .ds-helptext__icon {
- display: none;
-}
-
-.ds-helptext__button:where(:hover, :focus, [data-state^='open']) > .ds-helptext__icon--filled {
- display: inline-block;
-}
-
-.ds-helptext__content {
- color: var(--dsc-helptext-color);
- width: fit-content;
- max-width: 700px;
-}
+ &:has(+ :popover-open)::before {
+ mask-image: var(--dsc-helptext-icon-url), linear-gradient(currentcolor, currentcolor); /* Cut icon out of currentcolor surface */
+ }
-.ds-helptext--sm .ds-helptext__icon {
- --dsc-helptext-icon-width: var(--ds-sizing-6);
- --dsc-helptext-icon-height: var(--ds-sizing-6);
-}
+ &[data-size='sm'] {
+ --dsc-helptext-size: var(--ds-sizing-6);
+ }
-.ds-helptext--md .ds-helptext__icon {
- --dsc-helptext-icon-width: var(--ds-sizing-7);
- --dsc-helptext-icon-height: var(--ds-sizing-7);
-}
+ &[data-size='lg'] {
+ --dsc-helptext-size: var(--ds-sizing-8);
+ }
-.ds-helptext--lg .ds-helptext__icon {
- --dsc-helptext-icon-width: var(--ds-sizing-8);
- --dsc-helptext-icon-height: var(--ds-sizing-8);
+ @media print {
+ display: none;
+ }
}
diff --git a/packages/react/src/components/HelpText/HelpText.mdx b/packages/react/src/components/HelpText/HelpText.mdx
index fd8b6e4375..b2369d2d81 100644
--- a/packages/react/src/components/HelpText/HelpText.mdx
+++ b/packages/react/src/components/HelpText/HelpText.mdx
@@ -8,9 +8,3 @@ import * as HelpTextStories from './HelpText.stories.tsx';
-
-## Portal
-
-Aktiver `portal` dersom innholdet blir klippet av andre elementer for at `Popover` skal vises øverst.
-
-
diff --git a/packages/react/src/components/HelpText/HelpText.stories.tsx b/packages/react/src/components/HelpText/HelpText.stories.tsx
index 9325684ae7..cdab113f32 100644
--- a/packages/react/src/components/HelpText/HelpText.stories.tsx
+++ b/packages/react/src/components/HelpText/HelpText.stories.tsx
@@ -19,18 +19,9 @@ export default {
export const Preview: Story = {
args: {
- title: 'Help text title',
+ 'aria-label': 'Help text title',
children: 'Help text content',
size: 'md',
},
decorators,
};
-
-export const Portal: Story = {
- args: {
- title: 'Help text title',
- children: 'Help text content',
- size: 'md',
- placement: 'top',
- },
-};
diff --git a/packages/react/src/components/HelpText/HelpText.test.tsx b/packages/react/src/components/HelpText/HelpText.test.tsx
index d2ed4db9d3..410f48e770 100644
--- a/packages/react/src/components/HelpText/HelpText.test.tsx
+++ b/packages/react/src/components/HelpText/HelpText.test.tsx
@@ -10,7 +10,7 @@ const render = (props: Partial = {}) => {
...props,
};
renderRtl(
-
+
Help
,
);
diff --git a/packages/react/src/components/HelpText/HelpText.tsx b/packages/react/src/components/HelpText/HelpText.tsx
index f0cd0b439b..fcd96cde35 100644
--- a/packages/react/src/components/HelpText/HelpText.tsx
+++ b/packages/react/src/components/HelpText/HelpText.tsx
@@ -1,18 +1,15 @@
import type { Placement } from '@floating-ui/utils';
import cl from 'clsx/lite';
-import { forwardRef, useId, useState } from 'react';
+import { forwardRef } from 'react';
import type { ButtonHTMLAttributes } from 'react';
import { Popover, type PopoverProps } from '../Popover';
-import { Paragraph } from '../Typography/Paragraph';
-
-import { HelpTextIcon } from './HelpTextIcon';
export type HelpTextProps = {
/**
- * Title for screen readers.
+ * Required descriptive label for screen readers.
**/
- title: string;
+ 'aria-label': string;
/**
* Size of the helptext
* @default md
@@ -23,68 +20,26 @@ export type HelpTextProps = {
* @default 'right'
*/
placement?: Placement;
-} & ButtonHTMLAttributes;
+} & Omit, 'color'>;
export const HelpText = forwardRef(
function HelpText(
- {
- title,
- placement = 'right',
- size = 'md',
- className,
- children,
- ...rest
- }: HelpTextProps,
+ { placement = 'right', size = 'md', className, children, ...rest },
ref,
) {
- const [open, setOpen] = useState(false);
- const randomId = useId();
-
return (
- <>
-
- {/* TODO: Why is popover wrapped in paragraph here? */}
-
- setOpen(false)}
- open={open}
- placement={placement}
- size={size}
- variant='info'
- >
- {children}
-
-
- >
+ />
+
+ {children}
+
+
);
},
);
diff --git a/packages/react/src/components/HelpText/HelpTextIcon.test.tsx b/packages/react/src/components/HelpText/HelpTextIcon.test.tsx
deleted file mode 100644
index 4f40f72881..0000000000
--- a/packages/react/src/components/HelpText/HelpTextIcon.test.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { render as renderRtl, screen } from '@testing-library/react';
-
-import type { HelpTextIconProps } from './HelpTextIcon';
-import { HelpTextIcon } from './HelpTextIcon';
-
-// Test data:
-const className = 'test-class';
-const defaultProps: HelpTextIconProps = {
- className,
- openState: true,
-};
-
-describe('HelpTextIcon', () => {
- it('Renders an icon', () => {
- render();
- expect(getIcon()).toBeInTheDocument();
- });
-
- it('Renders with correct path when the `filled` property is `true`', () => {
- render({ filled: true });
- const path = getIcon().firstChild;
- expect(path).toHaveAttribute(
- 'd',
- expect.stringMatching(
- /^M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0Zm0 16/,
- ),
- );
- });
-
- it('Renders with correct path when the `filled` property is `false`', () => {
- render({ filled: false });
- const path = getIcon().firstChild;
- expect(path).toHaveAttribute(
- 'd',
- expect.stringMatching(
- /^M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0Zm0 2C/,
- ),
- );
- });
-
- it('Renders with given className', () => {
- render({ className });
- expect(getIcon()).toHaveClass(className);
- });
-
- it('Renders with data-state="open" when `openState` is `true`', () => {
- render({ openState: true });
- expect(getIcon()).toHaveAttribute('data-state', 'open');
- });
-
- it('Renders with data-state="closed" when `openState` is `false`', () => {
- render({ openState: false });
- expect(getIcon()).toHaveAttribute('data-state', 'closed');
- });
-
- const getIcon = () => screen.getByRole('img', { hidden: true });
-});
-
-const render = (props: Partial = {}) =>
- renderRtl();
diff --git a/packages/react/src/components/HelpText/HelpTextIcon.tsx b/packages/react/src/components/HelpText/HelpTextIcon.tsx
deleted file mode 100644
index 9dd6b3c5a4..0000000000
--- a/packages/react/src/components/HelpText/HelpTextIcon.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-export type HelpTextIconProps = {
- className: string;
- filled?: boolean;
- openState: boolean;
-};
-
-export const HelpTextIcon = ({
- className,
- filled = false,
- openState,
-}: HelpTextIconProps) => {
- const d = filled
- ? 'M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0Zm0 16a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm0-11c2.205 0 4 1.657 4 3.693 0 .986-.416 1.914-1.172 2.612l-.593.54-.294.28c-.477.466-.869.94-.936 1.417l-.01.144v.814h-1.991v-.814c0-1.254.84-2.214 1.675-3.002l.74-.68c.38-.35.59-.816.59-1.31 0-1.024-.901-1.856-2.01-1.856-1.054 0-1.922.755-2.002 1.71l-.006.145H8C8 6.657 9.794 5 12 5Z'
- : 'M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0Zm0 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2Zm0 14a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm0-11c2.205 0 4 1.657 4 3.693 0 .986-.416 1.914-1.172 2.612l-.593.54-.294.28c-.477.466-.869.94-.936 1.417l-.01.144v.814h-1.991v-.814c0-1.254.84-2.214 1.675-3.002l.74-.68c.38-.35.59-.816.59-1.31 0-1.024-.901-1.856-2.01-1.856-1.054 0-1.922.755-2.002 1.71l-.006.145H8C8 6.657 9.794 5 12 5Z';
-
- return (
-
- );
-};
-
-HelpTextIcon.displayName = 'HelpText.Icon';