Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Copybutton-komponent #1982

Merged
merged 47 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
cafb7b4
:sparkles: Boilerplate new component
KenAJoh May 11, 2023
cb3d410
:art: Inlined copy-code
KenAJoh May 11, 2023
b3624f3
:art: timeout functionality
KenAJoh May 11, 2023
6ad76c2
:art: CopyButton API
KenAJoh May 11, 2023
40e5dc1
:art: Prop for timeout length
KenAJoh May 11, 2023
a0418e8
:wheelchair: Dynamic aria-labels
KenAJoh May 11, 2023
0764585
:memo: Docs
KenAJoh May 11, 2023
62f6315
:recycle: Refactor stories
KenAJoh May 12, 2023
46f4428
:lipstick: Disabled styles
KenAJoh May 12, 2023
d44d6bc
:art: Play-tests for storybook
KenAJoh May 12, 2023
2e07548
:recycle: Refactor copy-function
KenAJoh May 12, 2023
de9ce7c
:recycle: Rename to action,neutral
KenAJoh May 12, 2023
998e44d
:memo: Added component to mappings
KenAJoh May 12, 2023
43cb4e7
:memo: Fallback tokens
KenAJoh May 12, 2023
aa8e4fe
:art: Tokens
KenAJoh May 12, 2023
6b79735
:memo: Docs
KenAJoh May 12, 2023
cc53e12
:memo: Deprecation-test
KenAJoh May 12, 2023
ba44207
:memo: Oppdatert deprecation-text
KenAJoh May 12, 2023
2ab6bfc
:art: CopyButton demoer
KenAJoh May 12, 2023
bcb5741
:bug: CSS-versions ble duplisert i version-dir
KenAJoh May 12, 2023
7980bd0
:lipstick: Darkmode
KenAJoh May 12, 2023
099f4d3
:fire: Fjernet ubrukt comment
KenAJoh May 12, 2023
f06260f
:art: Aksel-eksempler for CopyButton
KenAJoh May 12, 2023
b0541fb
:fire: Fjernet ubrukte stories fra storybook
KenAJoh May 12, 2023
bd84446
:memo: Changeset
KenAJoh May 12, 2023
0926039
Merge branch 'main' into copybutton
KenAJoh May 15, 2023
1b44c54
:memo: Endret clipboardText -> copyText
KenAJoh May 15, 2023
5af5ca2
:memo: Skrivefeil
KenAJoh May 15, 2023
cc0deaf
:memo: rename breadcrumbs-demo
KenAJoh May 15, 2023
5890336
:memo: rename breadcrumbs-demo
KenAJoh May 15, 2023
3094d42
:fire: Fjernet avansert aria-logikk, la inn title-props
KenAJoh May 15, 2023
54f9735
:fire: Fikset story
KenAJoh May 15, 2023
77dfb31
CopyButton-codemods (#1985)
KenAJoh May 15, 2023
8a97099
:memo: codemod docs
KenAJoh May 15, 2023
e0005b5
:wheelchair: aria-polite for bedre jaws-støtte
KenAJoh May 16, 2023
cca3998
:memo: Readme
KenAJoh May 16, 2023
a4334e8
:art: type button now default
KenAJoh May 16, 2023
88839da
:memo: Bedre dokumentasjon for tooltip-demo
KenAJoh May 16, 2023
da73030
:memo: Changeset text
KenAJoh May 16, 2023
03291a3
:art: Moved codemod-warnings to after jscodeshift
KenAJoh May 16, 2023
8e6cf28
:fire: Fjernet utkommentert kode
KenAJoh May 16, 2023
b746459
:art: reverserte endring i button.css
KenAJoh May 16, 2023
b6c48de
:bug: Fikset readme-markdown
KenAJoh May 16, 2023
e480a5c
:fire: Fjernet codemode fra website dependencies
KenAJoh May 16, 2023
df7cb18
:art: Byttet ut copybutton bruk på Aksel.nav.no med ny komponent
KenAJoh May 16, 2023
4ab6819
:art: useclient-directive
KenAJoh May 16, 2023
2b4a425
:arrow_up: Yarn lock
KenAJoh May 16, 2023
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
10 changes: 10 additions & 0 deletions .changeset/green-trainers-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@navikt/ds-css": minor
"@navikt/ds-react": minor
"@navikt/ds-react-internal": minor
---

:sparkles: Ny komponent `<CopyButton />`!

- Erstatter `<CopyToClipboard />` fra `@navikt/ds-react-internal`
- CopyToClipboard er markert som deprecated. Den vil fortsatt fungere, men noen lintere vil kunne på den.
2 changes: 1 addition & 1 deletion .storybook/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = {
addons: [
"@storybook/addon-a11y",
"@whitespace/storybook-addon-html",

"@storybook/addon-interactions",
{
name: "@storybook/addon-storysource",
options: {
Expand Down
5 changes: 5 additions & 0 deletions @navikt/core/css/config/_mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ const StyleMappings = {
component: "ContentContainer",
main: "content-container.css",
},
{
component: "CopyButton",
main: "copybutton.css",
dependencies: [typoCss],
},
{ component: "Detail", main: typoCss },
{ component: "ErrorMessage", main: typoCss },
{ component: "ErrorSummary", main: formCss, dependencies: [typoCss] },
Expand Down
5 changes: 4 additions & 1 deletion @navikt/core/css/config/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ async function bundleMinified() {
}

function copyToVersionFolder() {
const files = fastglob.sync("**/*.css", { cwd: `./${rootDir}` });
const files = fastglob.sync("**/*.css", {
cwd: `./${rootDir}`,
ignore: "**/version/**",
});

for (let file of files) {
const css = fs.readFileSync(`${rootDir}/${file}`, { encoding: "utf-8" });
Expand Down
129 changes: 129 additions & 0 deletions @navikt/core/css/copybutton.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
[data-theme="dark"] .navds-copybutton,
[data-theme="dark"].navds-copybutton {
--a-text-action: var(--a-blue-300);
--a-surface-hover: rgb(214 231 255 /0.13);
--a-icon-success: rgb(51 170 95 /1);
--a-text-subtle: rgb(231 240 254 /0.69);
--a-text-default: rgb(251 252 254 /0.96);
}

.navds-copybutton {
--__ac-copybutton-padding: var(--a-spacing-3) var(--a-spacing-5);

cursor: pointer;
margin: 0;
text-decoration: none;
border: none;
background: none;
border-radius: var(--ac-copybutton-border-radius, var(--a-border-radius-medium));
padding: var(--__ac-copybutton-padding);
display: grid;
place-content: center;
}

.navds-copybutton--small {
--__ac-copybutton-padding: var(--a-spacing-1) var(--a-spacing-3);

min-height: 2rem;
}

.navds-copybutton--icon-only {
--__ac-copybutton-padding: var(--a-spacing-3);
}

.navds-copybutton--small.navds-copybutton--icon-only {
--__ac-copybutton-padding: var(--a-spacing-1);
}

.navds-copybutton__icon {
font-size: 1.5rem;
display: flex;
margin-left: -4px;
}

.navds-copybutton__icon:only-child {
margin: 0;
}

.navds-copybutton:focus-visible {
outline: none;
box-shadow: var(--a-shadow-focus);
}

@supports not selector(:focus-visible) {
.navds-copybutton:focus {
outline: none;
box-shadow: var(--a-shadow-focus);
}
}

/* Variant/action */
.navds-copybutton--action {
color: var(--ac-copybutton-action-text, var(--a-text-action));
background-color: var(--ac-copybutton-action-bg, var(--a-surface-transparent));
}

.navds-copybutton--action:hover {
color: var(--ac-copybutton-action-hover-text, var(--a-text-action));
background-color: var(--ac-copybutton-action-hover-bg, var(--a-surface-hover));
}

.navds-copybutton--action:where(:disabled),
.navds-copybutton--action:hover:where(:disabled) {
color: var(--ac-copybutton-action-text, var(--a-text-action));
background-color: var(--ac-copybutton-action-bg, var(--a-surface-transparent));
box-shadow: none;
}

.navds-copybutton--active.navds-copybutton--action {
color: var(--ac-copybutton-action-active-text, var(--a-icon-success));
}

/* Variant/neutral */
.navds-copybutton--neutral {
color: var(--ac-copybutton-neutral-text, var(--a-text-subtle));
background-color: var(--ac-copybutton-neutral-bg, var(--a-surface-transparent));
}

.navds-copybutton--neutral:hover {
color: var(--ac-copybutton-neutral-hover-text, var(--a-text-default));
background-color: var(--ac-copybutton-neutral-hover-bg, var(--a-surface-hover));
}

.navds-copybutton--neutral:where(:disabled, .navds-copybutton--disabled),
.navds-copybutton--neutral:hover:where(:disabled, .navds-copybutton--disabled) {
color: var(--ac-copybutton-neutral-text, var(--a-text-default));
background-color: var(--ac-copybutton-neutral-bg, var(--a-surface-transparent));
box-shadow: none;
}

.navds-copybutton--active.navds-copybutton--neutral {
color: var(--ac-copybutton-neutral-active-text, var(--a-text-default));
}

.navds-copybutton__content {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--a-spacing-2);
}

.navds-copybutton--active > .navds-copybutton__content {
animation: var(--ac-copybutton-animation, akselCopyButtonAnimation 0.4s linear);
}

@keyframes akselCopyButtonAnimation {
0% {
opacity: 0.4;
}

100% {
opacity: 1;
}
}

/* Disabled */
.navds-copybutton:where(:disabled) {
cursor: not-allowed;
opacity: 0.3;
}
1 change: 1 addition & 0 deletions @navikt/core/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@import "content-container.css";
@import "chat.css";
@import "chips.css";
@import "copybutton.css";
@import "expansioncard.css";
@import "guide-panel.css";
@import "form/index.css";
Expand Down
14 changes: 14 additions & 0 deletions @navikt/core/css/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@
"--ac-chip-removable-neutral-hover-bg": "--a-surface-neutral-subtle-hover",
"--ac-chip-removable-neutral-hover-border": "--a-border-strong"
},
"copybutton": {
"--ac-copybutton-border-radius": "--a-border-radius-medium",
"--ac-copybutton-action-text": "--a-text-action",
"--ac-copybutton-action-bg": "--a-surface-transparent",
"--ac-copybutton-action-hover-text": "--a-text-action",
"--ac-copybutton-action-hover-bg": "--a-surface-hover",
"--ac-copybutton-action-active-text": "--a-icon-success",
"--ac-copybutton-neutral-text": "--a-text-subtle",
"--ac-copybutton-neutral-bg": "--a-surface-transparent",
"--ac-copybutton-neutral-hover-text": "--a-text-default",
"--ac-copybutton-neutral-hover-bg": "--a-surface-hover",
"--ac-copybutton-neutral-active-text": "--a-text-default",
"--ac-copybutton-animation": "akselCopyButtonAnimation 0.4s linear"
},
"date": {
"--ac-date-middle-text": "--a-text-on-action",
"--ac-date-middle-bg": "--a-surface-action-selected",
Expand Down
164 changes: 164 additions & 0 deletions @navikt/core/react/src/copybutton/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons";
import cl from "clsx";
import React, {
ButtonHTMLAttributes,
forwardRef,
useEffect,
useRef,
useState,
} from "react";
import copy from "../util/copy";
import Label from "../typography/Label";

export interface CopyButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement> {
/**
* @default "medium"
*/
size?: "medium" | "small";
/**
* @default "neutral"
*/
variant?: "action" | "neutral";
KenAJoh marked this conversation as resolved.
Show resolved Hide resolved
/**
* Text to copy to clipboard
*/
clipboardText: string;
/**
* Optional text in button
*/
text?: string;
/**
* Text shown when button is clicked
* Only set if used with 'text'-prop
*/
activeText?: string;
/**
* Callback when 'copied'-state is active
*/
onActiveChange?: (state: boolean) => void;
/**
* Icon shown when button is not clicked
* @default <FilesIcon />
*/
icon?: React.ReactNode;
/**
* Icon shown when active
* @default <CheckmarkIcon />
*/
activeIcon?: React.ReactNode;
/**
* Timeout duration in milliseconds
* @default 2000
*/
activeDuration?: number;
}

export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
(
{
className,
clipboardText,
text,
activeText = "Kopiert!",
variant = "neutral",
size = "medium",
onActiveChange,
icon,
activeIcon,
activeDuration = 2000,
...rest
},
ref
) => {
const [active, setActive] = useState(false);
const [activated, setActivated] = useState(false);
const timeoutRef = useRef<number>();

useEffect(() => {
return () => {
timeoutRef.current && clearTimeout(timeoutRef.current);
};
}, []);

const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
timeoutRef.current && clearTimeout(timeoutRef.current);
copy(clipboardText);
setActive(true);
rest.onClick?.(event);
onActiveChange?.(true);
setActivated(false);

timeoutRef.current = window.setTimeout(() => {
setActivated(true);
setActive(false);
onActiveChange?.(false);
}, activeDuration);
};

return (
<button
{...rest}
ref={ref}
className={cl(
"navds-copybutton",
className,
`navds-copybutton--${size}`,
`navds-copybutton--${variant}`,
{
"navds-copybutton--icon-only": !text,
"navds-copybutton--active": active,
}
)}
onClick={handleClick}
aria-label={activated ? activeText : undefined}
onBlur={() => setActivated(false)}
>
<span className="navds-copybutton__content">
{active ? (
<span className="navds-copybutton__icon">
{activeIcon ?? (
<CheckmarkIcon
aria-hidden={!!text}
title={text ? undefined : "Kopiert"}
/>
)}
</span>
) : (
<span className="navds-copybutton__icon">
{icon ?? (
<FilesIcon
aria-hidden={!!text}
title={text ? undefined : "Kopier"}
/>
)}
</span>
)}

{text &&
(active ? (
<Label
as="span"
size={size === "medium" ? "medium" : "small"}
aria-live="polite"
>
{activeText}
</Label>
) : (
<Label
as="span"
size={size === "medium" ? "medium" : "small"}
aria-live="polite"
>
{text}
</Label>
))}
</span>
</button>
);
}
);

export default CopyButton;
Loading