Compare commits

...
Author SHA1 Message Date
github-actions[bot] dd14eb272e Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.14
 - @navikt/ds-react@0.17.18
 - @navikt/ds-tailwind@0.1.8
 - @navikt/ds-tokens@0.8.6
 - @navikt/ds-css-internal@0.7.2
 - @navikt/ds-react-internal@0.12.18
2022-04-01 07:38:03 +00:00
Andreas NordahlandKen 022d3a6a6f [fix] New/suggestfield (#1472)
* satt opp react

* stylet suggest-field

* stylet inverted versjon

* inverted label-farge

* bedre eksempel

* lint fix

* fikset border på inverted

* bruker spacing

* form -> div

* fikset stories

* fjernet clearbutton

* fikset story

* wip darkmode and combined search and suggest field

* no-button fixes

* paddings

* icon sizes

* default value clear bug

* focus

* placeholder

* search icon color

* primary and secondary button

* update tokens from figma

* purge suggest field

* internal header with data-theme dark

* fix some dark theme text color issues

* stylelint

* dark clear hover color

* no-button -> simple

Co-authored-by: Ken <ken.aleksander@gmail.com>
2022-04-01 09:29:45 +02:00
github-actions[bot] fcbd614a34 Lerna versionbump [ci skip]
- @navikt/ds-css@0.16.13
 - @navikt/ds-react@0.17.17
 - @navikt/ds-tailwind@0.1.7
 - @navikt/ds-tokens@0.8.5
 - @navikt/ds-react-internal@0.12.17
2022-03-31 10:50:28 +00:00
0a9aa6d3a1 [FIX] Tooltip komponent (#1462)
Co-authored-by: Andreas Nordahl <andnorda@gmail.com>
Co-authored-by: Ken <26967723+KenAJoh@users.noreply.github.com>
Co-authored-by: Ken <ken.aleksander@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Martin Berglund <martin.berglund@nav.no>
Co-authored-by: larseirikhansen <71271458+larseirikhansen@users.noreply.github.com>
2022-03-31 11:38:09 +02:00
Ida 926437f06f Update README.md (#1475) 2022-03-31 11:34:34 +02:00
Ken 288456a460 type button på readmore (#1473) 2022-03-30 14:46:01 +02:00
github-actions[bot] 61f6b62c9e Lerna versionbump [ci skip]
- @navikt/ds-react@0.17.16
 - @navikt/ds-react-internal@0.12.16
2022-03-28 15:02:20 +00:00
Andreas Nordahl dc9ffdebaf [fix] expandable icon title (#1471) 2022-03-28 16:53:05 +02:00
30 changed files with 808 additions and 93 deletions

No files matched your search

+13 -1
View File
@@ -20,10 +20,22 @@ html {
box-sizing: inherit;
}
:root,
[data-theme="light"] {
--navds-shadow-focus: 0 0 0 3px var(--navds-semantic-color-focus);
color: var(--navds-semantic-color-text);
}
[data-theme="dark"] {
--navds-shadow-focus: var(--navds-shadow-focus-inverted);
color: var(--navds-semantic-color-text-inverted);
}
body {
font-family: var(--navds-font-family);
line-height: 1.333;
color: var(--navds-semantic-color-text);
font-size: 1.125rem;
}
+35 -1
View File
@@ -1,4 +1,5 @@
:root {
:root,
[data-theme="light"] {
/* Primary */
--navds-button-color-primary-text: var(--navds-semantic-color-text-inverted);
--navds-button-color-primary-border-focus: var(
@@ -80,6 +81,39 @@
--navds-loader-color-on-button-foreground: currentColor;
}
[data-theme="dark"] {
/* Primary */
--navds-button-color-primary-text: var(--navds-semantic-color-text);
--navds-button-color-primary-border-focus: var(--navds-semantic-color-text);
--navds-button-color-primary-background: var(--navds-global-color-blue-200);
--navds-button-color-primary-background-hover: var(
--navds-global-color-blue-300
);
--navds-button-color-primary-background-active: var(
--navds-global-color-blue-400
);
/* Secondary */
--navds-button-color-secondary-text: var(--navds-global-color-blue-200);
--navds-button-color-secondary-text-hover: var(--navds-global-color-blue-200);
--navds-button-color-secondary-text-active: var(
--navds-global-color-blue-200
);
--navds-button-color-secondary-border: var(--navds-global-color-blue-200);
--navds-button-color-secondary-border-focus-active-hover: var(
--navds-semantic-color-text
);
--navds-button-color-secondary-background: var(
--navds-semantic-color-component-background-inverted
);
--navds-button-color-secondary-background-hover: var(
--navds-global-color-blue-900
);
--navds-button-color-secondary-background-active: var(
--navds-global-color-blue-800
);
}
.navds-button {
display: inline-flex;
cursor: pointer;
+47 -14
View File
@@ -1,8 +1,18 @@
:root {
:root,
[data-theme="light"] {
--navds-search-color-text: var(--navds-semantic-color-text);
--navds-search-color-border: var(--navds-semantic-color-border);
--navds-search-color-border-hover: var(
--navds-semantic-color-interaction-primary
);
--navds-search-color-clear-hover: var(--navds-global-color-blue-500);
}
[data-theme="dark"] {
--navds-search-color-text: var(--navds-semantic-color-text-inverted);
--navds-search-color-border: var(--navds-semantic-color-border-inverted);
--navds-search-color-border-hover: var(--navds-global-color-blue-200);
--navds-search-color-clear-hover: var(--navds-global-color-blue-200);
}
.navds-search {
@@ -22,22 +32,45 @@
}
.navds-search__input {
padding-right: var(--navds-spacing-12);
padding-right: var(--navds-spacing-10);
}
.navds-form-field--small .navds-search__input {
padding-right: var(--navds-spacing-8);
}
.navds-search__input--simple {
padding-left: var(--navds-spacing-11);
}
.navds-form-field--small .navds-search__input--simple {
padding-left: var(--navds-spacing-7);
}
.navds-search__input:not(.navds-search__input--simple) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.navds-form-field--small .navds-search__input {
min-height: 32px;
padding: 0.15rem;
padding-right: var(--navds-spacing-8);
.navds-search__search-icon {
position: absolute;
left: var(--navds-spacing-3);
top: 50%;
transform: translateY(-50%);
pointer-events: none;
font-size: 1.5rem;
}
.navds-form-field--small .navds-search__search-icon {
left: var(--navds-spacing-2);
font-size: 1rem;
}
.navds-search__button-clear {
position: absolute;
right: 0.75rem;
border-radius: var(--navds-border-radius-medium);
color: var(--navds-semantic-color-text);
color: var(--navds-search-color-text);
height: 32px;
width: 32px;
top: 50%;
@@ -59,7 +92,7 @@
}
.navds-search__button-clear:hover {
color: var(--navds-global-color-blue-500);
color: var(--navds-search-color-clear-hover);
}
.navds-search__button-clear:focus {
@@ -84,13 +117,13 @@
min-width: 40px;
}
.navds-search__button-search.navds-button--tertiary {
.navds-search__button-search.navds-button--secondary {
box-shadow: -1px 0 0 0 var(--navds-search-color-border) inset,
0 1px 0 0 var(--navds-search-color-border) inset,
0 -1px 0 0 var(--navds-search-color-border) inset;
}
.navds-search__button-search.navds-button--tertiary:hover {
.navds-search__button-search.navds-button--secondary:hover {
box-shadow: -1px 0 0 0 var(--navds-search-color-border-hover) inset,
0 1px 0 0 var(--navds-search-color-border-hover) inset,
0 -1px 0 0 var(--navds-search-color-border-hover) inset,
@@ -99,21 +132,21 @@
}
.navds-search__wrapper-inner:focus-within
+ .navds-search__button-search.navds-button--tertiary:hover {
+ .navds-search__button-search.navds-button--secondary:hover {
z-index: auto;
}
.navds-search__button-search.navds-button--tertiary:focus {
.navds-search__button-search.navds-button--secondary:focus {
box-shadow: 0 0 0 1px var(--navds-search-color-border) inset,
var(--navds-shadow-focus);
}
.navds-search__button-search.navds-button--tertiary:focus:hover {
.navds-search__button-search.navds-button--secondary:focus:hover {
box-shadow: 0 0 0 1px var(--navds-search-color-border-hover) inset,
var(--navds-shadow-focus);
}
.navds-search__button-search.navds-button--tertiary:focus:active {
.navds-search__button-search.navds-button--secondary:focus:active {
box-shadow: 0 0 0 1px white inset, var(--navds-shadow-focus);
}
+21 -3
View File
@@ -1,4 +1,5 @@
:root {
:root,
[data-theme="light"] {
--navds-text-field-color-text: var(--navds-semantic-color-text);
--navds-text-field-color-background: var(
--navds-semantic-color-component-background-light
@@ -18,11 +19,24 @@
--navds-semantic-color-component-background-alternate
);
--navds-text-field-color-text-disabled: var(--navds-semantic-color-text);
--navds-text-field-color-placeholder-text: var(
--navds-semantic-color-text-muted
);
}
[data-theme="dark"] {
--navds-text-field-color-text: var(--navds-semantic-color-text-inverted);
--navds-text-field-color-background: var(
--navds-semantic-color-component-background-dark
);
--navds-text-field-color-border: var(--navds-semantic-color-border-inverted);
--navds-text-field-color-border-hover: var(--navds-global-color-blue-200);
--navds-text-field-color-placeholder-text: var(--navds-global-color-gray-500);
}
.navds-text-field__input {
appearance: none;
padding: 0.5rem;
padding: var(--navds-spacing-2);
background-color: var(--navds-text-field-color-background);
border-radius: var(--navds-border-radius-medium);
border: 1px solid var(--navds-text-field-color-border);
@@ -31,7 +45,11 @@
color: var(--navds-text-field-color-text);
}
.navds-form-field--small > .navds-text-field__input {
.navds-text-field__input::placeholder {
color: var(--navds-text-field-color-placeholder-text);
}
.navds-form-field--small .navds-text-field__input {
padding: 0 0.25rem;
min-height: 32px;
}
+1
View File
@@ -17,6 +17,7 @@
@import "pagination.css";
@import "popover.css";
@import "tag.css";
@import "tooltip.css";
@import "toggle-group.css";
@import "panel.css";
@import "link-panel.css";
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css",
"version": "0.16.12",
"version": "0.16.14",
"private": false,
"description": "Css for NAV Designsystem components",
"author": "NAV Designsystem team",
@@ -21,7 +21,7 @@
"watch": "postcss --watch --use postcss-import -o dist/index.css index.css"
},
"devDependencies": {
"@navikt/ds-tokens": "^0.8.4",
"@navikt/ds-tokens": "^0.8.6",
"normalize.css": "^8.0.1",
"postcss": "^8.3.6",
"postcss-cli": "^8.3.1",
+56
View File
@@ -0,0 +1,56 @@
@keyframes tooltipFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.navds-tooltip {
z-index: var(--navds-z-index-tooltip);
}
.navds-tooltip__inner {
background-color: var(--navds-semantic-color-component-background-inverted);
color: var(--navds-semantic-color-text-inverted);
border-radius: var(--navds-border-radius-small);
padding: 0 var(--navds-spacing-2);
align-items: center;
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.05))
drop-shadow(0 1px 3px rgba(0, 0, 0, 0.1));
display: flex;
flex-direction: column;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
animation-duration: 0.2s;
animation-name: tooltipFadeIn;
text-align: center;
}
.navds-tooltip__arrow {
height: 7px;
width: 7px;
transform: rotate(45deg);
z-index: -1;
background-color: var(--navds-semantic-color-component-background-inverted);
position: absolute;
}
.navds-tooltip__keys {
padding-bottom: var(--navds-spacing-1);
display: flex;
gap: var(--navds-spacing-1);
}
.navds-tooltip__key {
font-family: var(--navds-font-family);
background-color: var(--navds-global-color-gray-700);
color: var(--navds-semantic-color-text-inverted);
border-radius: var(--navds-border-radius-small);
padding: 0 var(--navds-spacing-1);
min-width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
}
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react",
"version": "0.17.15",
"version": "0.17.18",
"private": false,
"description": "NAV designsystem react components",
"author": "NAV Designsystem team",
@@ -34,6 +34,7 @@
"test": "jest"
},
"dependencies": {
"@floating-ui/react-dom": "0.6.0",
"@material-ui/core": "^4.12.3",
"@navikt/ds-icons": "^0.8.6",
"@popperjs/core": "^2.10.1",
+22 -24
View File
@@ -1,10 +1,9 @@
import { Close } from "@navikt/ds-icons";
import { Close, Search as SearchIcon } from "@navikt/ds-icons";
import cl from "classnames";
import React, {
forwardRef,
InputHTMLAttributes,
useCallback,
useEffect,
useRef,
useState,
} from "react";
@@ -59,9 +58,9 @@ export interface SearchProps
clearButton?: boolean;
/**
* Changes button-variant
* @default "tertiary"
* @default "primary"
*/
variant?: "tertiary" | "primary";
variant?: "primary" | "secondary" | "simple";
}
interface SearchComponent
@@ -74,8 +73,8 @@ interface SearchComponent
export interface SearchContextProps {
disabled?: boolean;
size: "medium" | "small";
variant?: "tertiary" | "primary";
onSearch?: () => void;
variant: "primary" | "secondary" | "simple";
onSearch: () => void;
}
export const SearchContext = React.createContext<SearchContextProps | null>(
@@ -99,7 +98,9 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
clearButton = true,
children,
onSearch,
variant = "tertiary",
variant = "primary",
defaultValue,
onChange,
...rest
} = props;
@@ -107,26 +108,23 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
const mergedRef = mergeRefs([searchRef, ref]);
const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
const [controlledValue, setControlledValue] = useState(value ?? "");
const [internalValue, setInternalValue] = useState(defaultValue ?? "");
const handleChange = useCallback(
(v: string) => {
searchRef.current && value === undefined && setControlledValue(v);
props?.onChange?.(v);
value === undefined && setInternalValue(v);
onChange?.(v);
},
[props, value]
[onChange, value]
);
const handleClear = useCallback(
(event: SearchClearEvent) => {
onClear?.(event);
handleChange("");
if (searchRef.current && value === undefined) {
searchRef.current.value = "";
}
searchRef.current && searchRef.current?.focus?.();
},
[handleChange, onClear, value]
[handleChange, onClear]
);
useEventListener(
@@ -143,10 +141,6 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
wrapperRef
);
useEffect(() => {
value !== undefined && setControlledValue(value);
}, [value]);
return (
<div
ref={setWrapperRef}
@@ -184,23 +178,27 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
)}
<div className="navds-search__wrapper">
<div className="navds-search__wrapper-inner">
{variant === "simple" && (
<SearchIcon aria-hidden className="navds-search__search-icon" />
)}
<input
ref={mergedRef}
{...omit(rest, ["size"])}
{...inputProps}
{...(props.value !== undefined && { value: props.value })}
value={value ?? internalValue}
onChange={(e) => handleChange(e.target.value)}
type="search"
role="searchbox"
className={cl(
className,
"navds-search__input",
`navds-search__input--${variant}`,
"navds-text-field__input",
"navds-body-short",
`navds-body-${size ?? "medium"}`
`navds-body-${size}`
)}
/>
{controlledValue && clearButton && (
{(value ?? internalValue) && clearButton && (
<button
type="button"
onClick={(e) => handleClear({ trigger: "Click", event: e })}
@@ -218,10 +216,10 @@ const Search = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {
size,
disabled: inputProps.disabled,
variant,
onSearch: () => onSearch?.(controlledValue),
onSearch: () => onSearch?.(value ?? internalValue),
}}
>
{children ? children : <SearchButton />}
{children ? children : variant !== "simple" && <SearchButton />}
</SearchContext.Provider>
</div>
</div>
@@ -33,7 +33,7 @@ const SearchButton: SearchButtonType = forwardRef(
{...rest}
ref={ref}
size={size}
variant={variant}
variant={variant === "secondary" ? "secondary" : "primary"}
className={cl("navds-search__button-search", className)}
disabled={context?.disabled ?? disabled}
onClick={(e) => {
@@ -0,0 +1,52 @@
import { Meta } from "@storybook/react/types-6-0";
import React from "react";
import { Heading } from "../..";
import { Search } from "..";
export default {
title: "ds-react/form/search",
component: Search,
} as Meta;
export const LightTheme = () => (
<div
style={{ maxWidth: 400, display: "flex", flexDirection: "column", gap: 16 }}
>
<Heading size="xlarge">Search</Heading>
<Heading level="2" size="large">
size medium
</Heading>
<Search label="hidden label" placeholder="Søk" />
<Search label="hidden label" variant="secondary" defaultValue="dagpenger" />
<Search label="hidden label" variant="simple" placeholder="Søk" />
<Search label="with label" hideLabel={false} />
<Heading level="2" size="large">
size small
</Heading>
<Search label="hidden label" size="small" placeholder="Søk" />
<Search
label="hidden label"
variant="secondary"
size="small"
defaultValue="dagpenger"
/>
<Search
label="hidden label"
variant="simple"
size="small"
placeholder="Søk"
/>
<Search label="with label" hideLabel={false} size="small" />
</div>
);
export const DarkTheme = () => (
<div data-theme="dark">
<LightTheme />
</div>
);
DarkTheme.parameters = {
backgrounds: { default: "dark" },
};
@@ -21,11 +21,18 @@ export const All = () => {
<h1>Search</h1>
<Search label="Søk alle sider om X og Y" onSearch={console.log}></Search>
<h2>Primary search</h2>
<h2>Secondary</h2>
<Search
label="Søk alle sider om X og Y"
onSearch={console.log}
variant="primary"
variant="secondary"
></Search>
<h2>No button</h2>
<Search
label="Søk alle sider om X og Y"
onSearch={console.log}
variant="simple"
></Search>
<h2>Search small</h2>
@@ -42,7 +49,7 @@ export const All = () => {
description="Beskrivelse av søket"
size="small"
hideLabel
variant="primary"
variant="secondary"
>
<Search.Button />
</Search>
+1
View File
@@ -21,6 +21,7 @@ export * from "./tag";
export * from "./toggle-group";
export * from "./table";
export * from "./tabs";
export * from "./tooltip";
export * from "./typography";
export * from "./util";
@@ -55,6 +55,7 @@ const ReadMore = forwardRef<HTMLButtonElement, ReadMoreProps>(
return (
<>
<button
type="button"
{...rest}
className={cl(
"navds-read-more",
@@ -84,11 +84,11 @@ const ExpandableRow: ExpandableRowType = forwardRef(
>
<Expand
className="navds-table__expandable-icon"
title="Vis mer"
title={isOpen ? "Vis mindre" : "Vis mer"}
/>
<ExpandFilled
className="navds-table__expandable-icon navds-table__expandable-icon--filled"
title="Vis mindre"
title={isOpen ? "Vis mindre" : "Vis mer"}
/>
</button>
</DataCell>
+301
View File
@@ -0,0 +1,301 @@
import cl from "classnames";
import React, {
cloneElement,
forwardRef,
HTMLAttributes,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { composeEventHandlers, Detail, useEventListener } from "..";
import {
useFloating,
arrow as flArrow,
shift,
autoUpdate,
flip,
hide,
} from "@floating-ui/react-dom";
import mergeRefs from "react-merge-refs";
import Portal from "./portal";
import { useId } from "../util";
export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
/**
* Element tooltip anchors to
*/
children: React.ReactElement & React.RefAttributes<HTMLElement>;
/**
* Open state for contolled tooltip
*/
open?: boolean;
/**
* Tells tooltip to start in open state
* @note "open"-prop overwrites this
*/
defaultOpen?: boolean;
/**
* Orientation for tooltip
* @default "top"
*/
placement?: "top" | "right" | "bottom" | "left";
/**
* Toggles rendering of arrow
* @default true
*/
arrow?: boolean;
/**
* Distance from anchor to tooltip
* @default 10px with arrow, 2px without arrow
*/
offset?: number;
/**
* Content shown in tooltip
*/
content: string;
/**
* Sets max allowed character length
* @default 80
*/
maxChar?: number;
/**
* Adds a delay in milliseconds before opening tooltip
* @default 300
*/
delay?: number;
/**
* List of Keyboard-keys for shortcuts
*/
keys?: string[];
}
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
(
{
children,
className,
arrow: _arrow = true,
placement: _placement = "top",
open,
defaultOpen = false,
offset: _offset,
content,
delay = 150,
id,
keys,
maxChar = 80,
...rest
},
ref
) => {
const arrowRef = useRef<HTMLDivElement | null>(null);
const [isOpen, setIsOpen] = useState(defaultOpen);
const openTimerRef = useRef(0);
const leaveTimerRef = useRef(0);
const isMouseDownRef = useRef(false);
const ariaId = useId(id);
const {
x,
y,
update,
placement,
refs,
middlewareData: {
arrow: { x: arrowX, y: arrowY } = {},
hide: { referenceHidden } = {},
},
} = useFloating({
placement: _placement,
middleware: [
shift(),
flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
flArrow({ element: arrowRef, padding: 5 }),
hide(),
],
});
/* https://floating-ui.com/docs/react-dom#updating */
useEffect(() => {
if (!refs.reference.current || !refs.floating.current) {
return;
}
// Only call this when the floating element is rendered
return autoUpdate(refs.reference.current, refs.floating.current, update);
}, [refs.reference, refs.floating, update, open, isOpen]);
const handleOpen = useCallback(() => {
window.clearTimeout(openTimerRef.current);
window.clearTimeout(leaveTimerRef.current);
setIsOpen(true);
}, [setIsOpen]);
const handleDelayedOpen = useCallback(() => {
window.clearTimeout(openTimerRef.current);
window.clearTimeout(leaveTimerRef.current);
openTimerRef.current = window.setTimeout(() => {
setIsOpen(true);
}, delay);
}, [delay, setIsOpen]);
const handleClose = useCallback(() => {
window.clearTimeout(openTimerRef.current);
leaveTimerRef.current = window.setTimeout(() => {
setIsOpen(false);
}, 50);
}, [setIsOpen]);
const handleMouseUp = useCallback(
() => (isMouseDownRef.current = false),
[]
);
useEffect(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => window.clearTimeout(openTimerRef.current);
}, []);
useEffect(() => {
return () => document.removeEventListener("mouseup", handleMouseUp);
}, [handleMouseUp]);
useEventListener(
"keydown",
useCallback((e) => e.key === "Escape" && handleClose(), [handleClose]),
document
);
/* https://floating-ui.com/docs/react-dom#stable-ref-prop */
const stableRef = useMemo(() => mergeRefs([ref, refs.floating]), [
ref,
refs.floating,
]);
if (
!children ||
children?.type === React.Fragment ||
(children as any) === React.Fragment
) {
console.error(
"<Tooltip> children needs to be a single ReactElement and not <React.Fragment/>/<></>"
);
return null;
}
if (content?.length > maxChar) {
console.error(
`Because of strict accessibility concers we encourage all Tooltips to have less than 80 characters. Can be overwritten with the maxChar-prop`
);
return null;
}
return (
<>
{cloneElement(children, {
...children.props,
"aria-describedby":
open || isOpen
? cl(ariaId, children?.props["aria-describedby"])
: children?.props["aria-describedby"],
ref: mergeRefs([(children as any).ref, refs.reference]),
onMouseEnter: composeEventHandlers(
children.props.onMouseEnter,
handleDelayedOpen
),
onMouseLeave: composeEventHandlers(
children.props.onMouseLeave,
handleClose
),
onMouseDown: composeEventHandlers(children.props.onMouseDown, () => {
isMouseDownRef.current = true;
document.addEventListener("mouseup", handleMouseUp, { once: true });
}),
onFocus: composeEventHandlers(
children.props.onFocus,
() => !isMouseDownRef.current && handleOpen()
),
onBlur: composeEventHandlers(children.props.onBlur, handleClose),
})}
{(open ?? isOpen) && (
<Portal>
<div
ref={stableRef}
{...rest}
onMouseEnter={handleOpen}
onMouseLeave={handleClose}
role="tooltip"
id={ariaId}
style={{
position: "absolute",
top: y ?? "",
left: x ?? "",
visibility: referenceHidden ? "hidden" : "visible",
}}
data-side={placement}
className={cl(
"navds-tooltip",
"navds-detail navds-detail--small",
className
)}
>
<div
className="navds-tooltip__inner"
style={{
[{
top: "marginBottom",
right: "marginLeft",
bottom: "marginTop",
left: "marginRight",
}[placement]]: _offset ? _offset : _arrow ? 10 : 2,
}}
>
{content}
{keys && (
<span className="navds-tooltip__keys">
{keys.map((key) => (
<Detail
size="small"
as="kbd"
key={key}
className="navds-tooltip__key"
>
{key}
</Detail>
))}
</span>
)}
{_arrow && (
<div
ref={(node) => {
arrowRef.current = node;
}}
className="navds-tooltip__arrow"
style={{
left: arrowX != null ? `${arrowX}px` : "",
top: arrowY != null ? `${arrowY}px` : "",
right: "",
bottom: "",
[{
top: "bottom",
right: "left",
bottom: "top",
left: "right",
}[placement]]: "-3.5px",
}}
/>
)}
</div>
</div>
</Portal>
)}
</>
);
}
);
export default Tooltip;
+2
View File
@@ -0,0 +1,2 @@
export { default as Tooltip } from "./Tooltip";
export * from "./Tooltip";
+15
View File
@@ -0,0 +1,15 @@
/* https://github.com/radix-ui/primitives/blob/main/packages/react/portal/src/Portal.tsx */
import ReactDOM from "react-dom";
const Portal = ({ children }) => {
const hostElement = globalThis?.document?.body;
if (hostElement) {
return ReactDOM.createPortal(children, hostElement);
}
// bail out of ssr
return null;
};
export default Portal;
@@ -0,0 +1,144 @@
import React, { useRef, useState } from "react";
import { Tooltip } from "../index";
import { Meta } from "@storybook/react/types-6-0";
import { Refresh } from "@navikt/ds-icons";
import { Button } from "../..";
import { ToggleGroup } from "../toggle-group";
export default {
title: "ds-react/tooltip",
component: Tooltip,
parameters: {
chromatic: { disable: true },
},
} as Meta;
export const Demo = () => {
const [open, setOpen] = useState(true);
const testRef = useRef(null);
return (
<div
style={{
width: "100vw",
height: "100vh",
display: "flex",
flexDirection: "column",
gap: 32,
justifyContent: "center",
alignItems: "center",
}}
>
<Tooltip
content="Tooltip example"
keys={["Cmd", "K"]}
placement="right"
open={open}
ref={testRef}
>
<Button aria-describedby="test123" onClick={() => setOpen(!open)}>
Tooltip C
</Button>
</Tooltip>
<Tooltip
content="Tooltip example"
keys={["Cmd", "K"]}
placement="right"
defaultOpen
>
<Button onClick={() => console.log(testRef.current)}>Tooltip C</Button>
</Tooltip>
<ToggleGroup onChange={null} defaultValue="321">
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="123">Tekst</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="321">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3212">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3213">tekst 2</ToggleGroup.Item>
</Tooltip>
<Tooltip content="Tooltip" placement="bottom">
<ToggleGroup.Item value="3214">tekst 2</ToggleGroup.Item>
</Tooltip>
</ToggleGroup>
</div>
);
};
export const All = () => {
const [open, setOpen] = useState(false);
return (
<div style={{ margin: "4rem 8rem 4rem 8rem" }}>
<h2>Controlled</h2>
<Tooltip open={open} content="Controlled tooltip example" placement="top">
<Button onClick={() => setOpen((x) => !x)}>Toggle tooltip</Button>
</Tooltip>
<h2>no arrow</h2>
<Tooltip content="no arrow" placement="top" arrow={false}>
<Button>Tooltip</Button>
</Tooltip>
<h2>Keys</h2>
<Tooltip content="Tooltip" placement="top" open keys={["Cmd", "K"]}>
<Button>Tooltip</Button>
</Tooltip>
<h2>more offset</h2>
<Tooltip content="Tooltip" placement="top" open offset={20}>
<Button>Tooltip</Button>
</Tooltip>
<h2>all placements</h2>
<div
style={{
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
gap: "3rem",
}}
>
{["top", "left", "bottom", "right"].map((placement) => (
<div key={placement}>
<h3>{placement}</h3>
<div
style={{
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
gap: "3rem",
}}
>
<Tooltip
key={placement}
defaultOpen
content={placement}
placement={placement as any}
>
<Refresh aria-hidden tabIndex={0} />
</Tooltip>
</div>
</div>
))}
</div>
</div>
);
};
export const UUDemo = () => {
return (
<div>
<Button>Placeholder</Button>
<br />
<br />
<Tooltip content="Shortcut" placement="right" keys={["Cmd", "S"]}>
<Button>Lagre</Button>
</Tooltip>
<br />
<br />
<Button>Placeholder</Button>
</div>
);
};
+14
View File
@@ -44,3 +44,17 @@ export const useEventListener = <T extends ListenerT>(
};
}, [name, handler, target]);
};
/* https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx */
export const composeEventHandlers = <E>(
originalEventHandler?: (event: E) => void,
ourEventHandler?: (event: E) => void
) => {
return function handleEvent(event: E) {
originalEventHandler?.(event);
if (!((event as unknown) as Event).defaultPrevented) {
return ourEventHandler?.(event);
}
};
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tailwind",
"version": "0.1.6",
"version": "0.1.8",
"private": false,
"description": "Auto-generated tailwind config based on @navikt/ds-tokens",
"author": "NAV Designsystem team",
@@ -23,7 +23,7 @@
"directory": "@navikt/core/tailwind"
},
"devDependencies": {
"@navikt/ds-tokens": "^0.8.4",
"@navikt/ds-tokens": "^0.8.6",
"@types/jest": "^27.0.1",
"jest": "^27.2.0",
"lodash": "^4.17.21",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-tokens",
"version": "0.8.4",
"version": "0.8.6",
"private": false,
"description": "Auto-generated design-tokens for NAV design-systems framework",
"author": "NAV Designsystem team",
+1 -1
View File
@@ -396,7 +396,7 @@
},
"focus": {
"inverted": {
"value": "{navds.global.color.blue.200.value}"
"value": "{navds.global.color.blue.300.value}"
},
"@": {
"value": "{navds.global.color.blue.800.value}"
+1
View File
@@ -43,6 +43,7 @@ module.exports = {
modal: { value: 2000 },
popover: { value: 1000 },
focus: { value: 10 },
tooltip: { value: 3000 },
},
},
};
+1 -2
View File
@@ -1,9 +1,9 @@
:root {
--navdsi-header-color-text: var(--navds-semantic-color-text-inverted);
--navdsi-header-color-background: var(
--navds-semantic-color-component-background-inverted
);
--navdsi-header-color-border: var(--navds-semantic-color-border-muted);
--navdsi-header-color-text: var(--navds-semantic-color-text-inverted);
--navdsi-header-focus: inset 0 0 0 2px var(--navds-global-color-blue-100);
--navdsi-header-color-hover: var(--navds-global-color-gray-800);
--navdsi-header-color-active: var(--navds-global-color-gray-600);
@@ -37,7 +37,6 @@ button.navdsi-header__title {
}
.navdsi-header__user {
color: var(--navdsi-header-color-text);
padding: 0 var(--navds-spacing-5);
border-left: 1px solid var(--navdsi-header-color-border);
display: flex;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-css-internal",
"version": "0.7.1",
"version": "0.7.2",
"private": false,
"description": "Css for NAV internal Designsystem components",
"author": "NAV Designsystem team",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@navikt/ds-react-internal",
"version": "0.12.15",
"version": "0.12.18",
"private": false,
"description": "NAV internal designsystem react components",
"author": "NAV Designsystem team",
@@ -34,7 +34,7 @@
},
"dependencies": {
"@navikt/ds-icons": "^0.8.6",
"@navikt/ds-react": "^0.17.15",
"@navikt/ds-react": "^0.17.18",
"@popperjs/core": "^2.10.1",
"classnames": "^2.3.1",
"copy-to-clipboard": "^3.3.1",
+6 -1
View File
@@ -23,7 +23,12 @@ interface HeaderComponent
}
const Header = forwardRef(({ className, ...rest }, ref) => (
<header {...rest} ref={ref} className={cl("navdsi-header", className)} />
<header
data-theme="dark"
{...rest}
ref={ref}
className={cl("navdsi-header", className)}
/>
)) as HeaderComponent;
Header.Title = HeaderTitle;
+1 -1
View File
@@ -32,7 +32,7 @@ Repoet inneholder følgende:
## Endringslogg
[Alle relevante kode-endringer blir dokumentert her](https://design.nav.no/designsystem/side/endringslogg)
[Alle relevante kode-endringer blir dokumentert her](https://aksel.nav.no/designsystem/side/changelog)
## Bidra
+50 -30
View File
@@ -1312,6 +1312,26 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@floating-ui/core@^0.6.1":
version "0.6.1"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.6.1.tgz#aed861a1b6dbebadf14a9dd829ba67f4f3ae795c"
integrity sha512-Y30eVMcZva8o84c0HcXAtDO4BEzPJMvF6+B7x7urL2xbAqVsGJhojOyHLaoQHQYjb6OkqRq5kO+zeySycQwKqg==
"@floating-ui/dom@^0.4.0":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-0.4.1.tgz#3e164482d7f036527f65a794bdf054d2cc44fcbf"
integrity sha512-VAdIkn+zc4VB3pSemBk2JwjXlbVEBUbQft4V5jGpUeA9Vxd1fQD5eL3dMIMLVW003NYCOoW42JjjyBPNTpr8uQ==
dependencies:
"@floating-ui/core" "^0.6.1"
"@floating-ui/react-dom@0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-0.6.0.tgz#e58abf75bec1eac01f0adfff1454106bf9d3dd50"
integrity sha512-/dF+jpAUtoonjs2lO0F+miqEQlzA9XJGOxWBiJsq/COhK2kz7XzryyJdxfaNEH+RN63vRs9osDolOJXlst50hg==
dependencies:
"@floating-ui/dom" "^0.4.0"
use-isomorphic-layout-effect "^1.1.1"
"@gar/promisify@^1.0.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
@@ -4206,9 +4226,9 @@
"@types/react" "*"
"@types/react-dom@*":
version "17.0.13"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809"
integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==
version "17.0.12"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.12.tgz#f08dfbc3c6d35084ffd3db6f09bdea8913de8494"
integrity sha512-SeJ430ndLI15JtRSHuzotn7AIdUtr8bdk6XW8mMfzjZo3vahRgJGHZqHiI4nAzCHTVG4qC21ObfsHBVUEHcDhg==
dependencies:
"@types/react" "*"
@@ -4344,9 +4364,9 @@
source-map "^0.6.0"
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==
"@types/yargs@^15.0.0":
version "15.0.14"
@@ -5956,9 +5976,9 @@ camelize@^1.0.0:
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001312:
version "1.0.30001313"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz#a380b079db91621e1b7120895874e2fd62ed2e2f"
integrity sha512-rI1UN0koZUiKINjysQDuRi2VeSCce3bYJNmDcj3PIKREiAmjakugBul1QSkg/fPrlULYl6oWfGg3PbgOSY9X4Q==
version "1.0.30001312"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f"
integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==
capital-case@^1.0.4:
version "1.0.4"
@@ -7047,14 +7067,14 @@ cssstyle@^2.3.0:
cssom "~0.3.6"
csstype@^2.5.2, csstype@^2.5.7:
version "2.6.20"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
version "2.6.19"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa"
integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==
csstype@^3.0.2:
version "3.0.11"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33"
integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==
version "3.0.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
cyclist@^1.0.1:
version "1.0.1"
@@ -7653,10 +7673,10 @@ enhanced-resolve@^4.5.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
enhanced-resolve@^5.9.2:
version "5.9.2"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9"
integrity sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==
enhanced-resolve@^5.8.3:
version "5.9.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.1.tgz#e898cea44d9199fd92137496cff5691b910fb43e"
integrity sha512-jdyZMwCQ5Oj4c5+BTnkxPgDZO/BJzh/ADDmKebayyzNwjVX1AFCeGkOfxNx0mHi2+8BKC5VxUYiw3TIvoT7vhw==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
@@ -7915,9 +7935,9 @@ eslint-plugin-react-hooks@^4.1.2:
integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
eslint-plugin-react@^7.21.2:
version "7.29.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz#f4eab757f2756d25d6d4c2a58a9e20b004791f05"
integrity sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg==
version "7.29.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.2.tgz#2d4da69d30d0a736efd30890dc6826f3e91f3f7c"
integrity sha512-ypEBTKOy5liFQXZWMchJ3LN0JX1uPI6n7MN7OPHKacqXAxq5gYC30TdO7wqGYQyxD1OrzpobdHC3hDmlRWDg9w==
dependencies:
array-includes "^3.1.4"
array.prototype.flatmap "^1.2.5"
@@ -16339,9 +16359,9 @@ ts-pnp@^1.1.6:
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
tsconfig-paths@^3.12.0:
version "3.13.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7"
integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw==
version "3.12.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==
dependencies:
"@types/json5" "^0.0.29"
json5 "^1.0.1"
@@ -16783,7 +16803,7 @@ use-composed-ref@^1.0.0:
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849"
integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==
use-isomorphic-layout-effect@^1.0.0:
use-isomorphic-layout-effect@^1.0.0, use-isomorphic-layout-effect@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225"
integrity sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==
@@ -17153,9 +17173,9 @@ webpack@4:
webpack-sources "^1.4.1"
webpack@^5.4.0, webpack@^5.9.0:
version "5.70.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.70.0.tgz#3461e6287a72b5e6e2f4872700bc8de0d7500e6d"
integrity sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==
version "5.69.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5"
integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^0.0.51"
@@ -17166,7 +17186,7 @@ webpack@^5.4.0, webpack@^5.9.0:
acorn-import-assertions "^1.7.6"
browserslist "^4.14.5"
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.9.2"
enhanced-resolve "^5.8.3"
es-module-lexer "^0.9.0"
eslint-scope "5.1.1"
events "^3.2.0"