From 4f1cb0970c8572f156d5562372e9146ad00f444b Mon Sep 17 00:00:00 2001
From: Lisa Meyer <84317589+Lisa18289@users.noreply.github.com>
Date: Tue, 30 Jan 2024 16:13:04 +0100
Subject: [PATCH] feat(LabeledValue): Introduce component
---
.pnp.cjs | 22 ++++++++++
packages/components/.storybook/preview.tsx | 9 ++--
packages/components/package.json | 3 ++
.../src/components/CopyButton/CopyButton.tsx | 42 +++++++++++++++++++
.../src/components/CopyButton/index.ts | 3 ++
.../CopyButton/locales/de-DE.locale.json | 3 ++
.../CopyButton/locales/en-EN.locale.json | 3 ++
.../CopyButton/stories/Default.stories.tsx | 17 ++++++++
.../LabeledValue/LabeledValue.module.scss | 30 +++++++++++++
.../components/LabeledValue/LabeledValue.tsx | 36 ++++++++++++++++
.../src/components/LabeledValue/index.ts | 3 ++
.../LabeledValue/stories/Default.stories.tsx | 35 ++++++++++++++++
.../stories/EdgeCases.stories.tsx | 35 ++++++++++++++++
.../src/components/StatusIcon/StatusIcon.tsx | 6 +--
.../Tooltip/stories/Default.stories.tsx | 15 ++++---
.../Tooltip/stories/EdgeCases.stories.tsx | 13 +++---
packages/components/vite.build.config.ts | 2 +
.../src/components/labeled-value.yml | 5 +++
yarn.lock | 17 ++++++++
19 files changed, 280 insertions(+), 19 deletions(-)
create mode 100644 packages/components/src/components/CopyButton/CopyButton.tsx
create mode 100644 packages/components/src/components/CopyButton/index.ts
create mode 100644 packages/components/src/components/CopyButton/locales/de-DE.locale.json
create mode 100644 packages/components/src/components/CopyButton/locales/en-EN.locale.json
create mode 100644 packages/components/src/components/CopyButton/stories/Default.stories.tsx
create mode 100644 packages/components/src/components/LabeledValue/LabeledValue.module.scss
create mode 100644 packages/components/src/components/LabeledValue/LabeledValue.tsx
create mode 100644 packages/components/src/components/LabeledValue/index.ts
create mode 100644 packages/components/src/components/LabeledValue/stories/Default.stories.tsx
create mode 100644 packages/components/src/components/LabeledValue/stories/EdgeCases.stories.tsx
create mode 100644 packages/design-tokens/src/components/labeled-value.yml
diff --git a/.pnp.cjs b/.pnp.cjs
index 7dcd5c20d..3dcac0aa2 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -6122,6 +6122,7 @@ const RAW_RUNTIME_STATE =
["@types/rollup", "npm:0.54.0"],\
["clsx", "npm:2.1.0"],\
["concurrently", "npm:8.2.2"],\
+ ["copy-to-clipboard", "npm:3.3.3"],\
["decamelize", "npm:6.0.0"],\
["html-react-parser", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:5.1.1"],\
["jest", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:29.7.0"],\
@@ -6187,6 +6188,7 @@ const RAW_RUNTIME_STATE =
["@types/rollup", "npm:0.54.0"],\
["clsx", "npm:2.1.0"],\
["concurrently", "npm:8.2.2"],\
+ ["copy-to-clipboard", "npm:3.3.3"],\
["decamelize", "npm:6.0.0"],\
["html-react-parser", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:5.1.1"],\
["jest", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:29.7.0"],\
@@ -6258,6 +6260,7 @@ const RAW_RUNTIME_STATE =
["@types/rollup", "npm:0.54.0"],\
["clsx", "npm:2.1.0"],\
["concurrently", "npm:8.2.2"],\
+ ["copy-to-clipboard", "npm:3.3.3"],\
["decamelize", "npm:6.0.0"],\
["html-react-parser", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:5.1.1"],\
["jest", "virtual:160c7a696a99b065a822859af8c18a938041a659df75ba6216e289a2ba885865ddea0afdd5ed1924ae3f379fc9ec188b83f6e71e756a0bac8e9ca356654a6a90#npm:29.7.0"],\
@@ -17753,6 +17756,16 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["copy-to-clipboard", [\
+ ["npm:3.3.3", {\
+ "packageLocation": "./.yarn/cache/copy-to-clipboard-npm-3.3.3-6964e6cfad-3ebf5e8ee0.zip/node_modules/copy-to-clipboard/",\
+ "packageDependencies": [\
+ ["copy-to-clipboard", "npm:3.3.3"],\
+ ["toggle-selection", "npm:1.0.6"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["core-js-compat", [\
["npm:3.35.1", {\
"packageLocation": "./.yarn/cache/core-js-compat-npm-3.35.1-1088e0320e-c3b872e1f9.zip/node_modules/core-js-compat/",\
@@ -30108,6 +30121,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["toggle-selection", [\
+ ["npm:1.0.6", {\
+ "packageLocation": "./.yarn/cache/toggle-selection-npm-1.0.6-c506b73005-f2cf1f2c70.zip/node_modules/toggle-selection/",\
+ "packageDependencies": [\
+ ["toggle-selection", "npm:1.0.6"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["toidentifier", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/toidentifier-npm-1.0.1-f759712599-9393727993.zip/node_modules/toidentifier/",\
diff --git a/packages/components/.storybook/preview.tsx b/packages/components/.storybook/preview.tsx
index e8ffd89db..744740059 100644
--- a/packages/components/.storybook/preview.tsx
+++ b/packages/components/.storybook/preview.tsx
@@ -3,11 +3,10 @@ import React from "react";
const preview: Preview = {
decorators: [
- (Story) => (
-
-
-
- ),
+ (Story) => {
+ document.body.classList.add("flow");
+ return ;
+ },
],
globalTypes: {
rtlDirection: {},
diff --git a/packages/components/package.json b/packages/components/package.json
index 8fba27d54..389978ca4 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -8,6 +8,7 @@
"./Button": "./dist/Button.js",
"./Checkbox": "./dist/Checkbox.js",
"./Content": "./dist/Content.js",
+ "./CopyButton": "./dist/CopyButton.js",
"./FieldDescription": "./dist/FieldDescription.js",
"./FieldError": "./dist/FieldError.js",
"./Heading": "./dist/Heading.js",
@@ -15,6 +16,7 @@
"./Image": "./dist/Image.js",
"./Initials": "./dist/Initials.js",
"./Label": "./dist/Label.js",
+ "./LabeledValue": "./dist/LabeledValue.js",
"./Link": "./dist/Link.js",
"./Navigation": "./dist/Navigation.js",
"./Note": "./dist/Note.js",
@@ -48,6 +50,7 @@
"@react-aria/utils": "^3.23.0",
"@react-types/shared": "^3.22.0",
"clsx": "^2.1.0",
+ "copy-to-clipboard": "^3.3.3",
"html-react-parser": "^5.1.1",
"react-aria": "^3.31.1",
"react-aria-components": "^1.0.1",
diff --git a/packages/components/src/components/CopyButton/CopyButton.tsx b/packages/components/src/components/CopyButton/CopyButton.tsx
new file mode 100644
index 000000000..3136f2dba
--- /dev/null
+++ b/packages/components/src/components/CopyButton/CopyButton.tsx
@@ -0,0 +1,42 @@
+import React, { FC } from "react";
+import copy from "copy-to-clipboard";
+import { Button } from "@/components/Button";
+import { Icon } from "@/components/Icon";
+import { faCopy } from "@fortawesome/free-regular-svg-icons/faCopy";
+import locales from "./locales/*.locale.json";
+import { useLocalizedStringFormatter } from "react-aria";
+import { Tooltip, TooltipTrigger } from "@/components/Tooltip";
+
+export interface CopyButtonProps {
+ value: string;
+ className?: string;
+}
+
+export const CopyButton: FC = (props) => {
+ const { value, className } = props;
+
+ const stringFormatter = useLocalizedStringFormatter(locales);
+
+ const tooltip = stringFormatter.format("copyButton.copy");
+
+ const copyValue = () => {
+ copy(value);
+ };
+
+ return (
+
+
+ {tooltip}
+
+ );
+};
+
+export default CopyButton;
diff --git a/packages/components/src/components/CopyButton/index.ts b/packages/components/src/components/CopyButton/index.ts
new file mode 100644
index 000000000..92738b743
--- /dev/null
+++ b/packages/components/src/components/CopyButton/index.ts
@@ -0,0 +1,3 @@
+import { CopyButton } from "./CopyButton";
+export { type CopyButtonProps, CopyButton } from "./CopyButton";
+export default CopyButton;
diff --git a/packages/components/src/components/CopyButton/locales/de-DE.locale.json b/packages/components/src/components/CopyButton/locales/de-DE.locale.json
new file mode 100644
index 000000000..b1b6b4965
--- /dev/null
+++ b/packages/components/src/components/CopyButton/locales/de-DE.locale.json
@@ -0,0 +1,3 @@
+{
+ "copyButton.copy": "Kopieren"
+}
diff --git a/packages/components/src/components/CopyButton/locales/en-EN.locale.json b/packages/components/src/components/CopyButton/locales/en-EN.locale.json
new file mode 100644
index 000000000..d77bca104
--- /dev/null
+++ b/packages/components/src/components/CopyButton/locales/en-EN.locale.json
@@ -0,0 +1,3 @@
+{
+ "copyButton.copy": "Copy"
+}
diff --git a/packages/components/src/components/CopyButton/stories/Default.stories.tsx b/packages/components/src/components/CopyButton/stories/Default.stories.tsx
new file mode 100644
index 000000000..df56fdde3
--- /dev/null
+++ b/packages/components/src/components/CopyButton/stories/Default.stories.tsx
@@ -0,0 +1,17 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import React from "react";
+import { CopyButton } from "../CopyButton";
+
+const meta: Meta = {
+ title: "Buttons/CopyButton",
+ component: CopyButton,
+ render: (props) => ,
+ parameters: {
+ controls: { exclude: ["value", "className"] },
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
diff --git a/packages/components/src/components/LabeledValue/LabeledValue.module.scss b/packages/components/src/components/LabeledValue/LabeledValue.module.scss
new file mode 100644
index 000000000..17db89cfa
--- /dev/null
+++ b/packages/components/src/components/LabeledValue/LabeledValue.module.scss
@@ -0,0 +1,30 @@
+@import "@/styles";
+
+.labeledValue {
+ display: grid;
+ grid-template-areas:
+ "label"
+ "value";
+ row-gap: var(--labeled-value--label-to-value-spacing);
+ column-gap: var(--labeled-value--value-to-button-spacing);
+ grid-template-columns: auto 1fr;
+
+ &:has(.button) {
+ grid-template-areas:
+ "label label"
+ "value button";
+ }
+}
+
+.label {
+ grid-area: label;
+}
+
+.content {
+ grid-area: value;
+}
+
+.button {
+ grid-area: button;
+ justify-self: start;
+}
diff --git a/packages/components/src/components/LabeledValue/LabeledValue.tsx b/packages/components/src/components/LabeledValue/LabeledValue.tsx
new file mode 100644
index 000000000..aea79cab6
--- /dev/null
+++ b/packages/components/src/components/LabeledValue/LabeledValue.tsx
@@ -0,0 +1,36 @@
+import React, { FC, PropsWithChildren } from "react";
+import styles from "./LabeledValue.module.scss";
+import clsx from "clsx";
+import { PropsContext, PropsContextProvider } from "@/lib/propsContext";
+
+export interface LabeledValueProps extends PropsWithChildren {
+ className?: string;
+}
+
+export const LabeledValue: FC = (props) => {
+ const { children, className } = props;
+
+ const rootClassName = clsx(styles.labeledValue, className);
+
+ const propsContext: PropsContext = {
+ Label: {
+ className: styles.label,
+ },
+ Content: {
+ className: styles.content,
+ },
+ Button: {
+ className: styles.button,
+ },
+ };
+
+ return (
+
+ );
+};
+
+export default LabeledValue;
diff --git a/packages/components/src/components/LabeledValue/index.ts b/packages/components/src/components/LabeledValue/index.ts
new file mode 100644
index 000000000..668be3b3d
--- /dev/null
+++ b/packages/components/src/components/LabeledValue/index.ts
@@ -0,0 +1,3 @@
+import { LabeledValue } from "./LabeledValue";
+export { type LabeledValueProps, LabeledValue } from "./LabeledValue";
+export default LabeledValue;
diff --git a/packages/components/src/components/LabeledValue/stories/Default.stories.tsx b/packages/components/src/components/LabeledValue/stories/Default.stories.tsx
new file mode 100644
index 000000000..9c2146ef9
--- /dev/null
+++ b/packages/components/src/components/LabeledValue/stories/Default.stories.tsx
@@ -0,0 +1,35 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import LabeledValue from "../LabeledValue";
+import React from "react";
+import { Label } from "@/components/Label";
+import { Content } from "@/components/Content";
+import { CopyButton } from "@/components/CopyButton";
+
+const meta: Meta = {
+ title: "Content/Labeled Value",
+ component: LabeledValue,
+ parameters: {
+ controls: { exclude: ["className"] },
+ },
+ render: (props) => (
+
+
+ My proSpace
+
+ ),
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {};
+
+export const WithCopyButton: Story = {
+ render: (props) => (
+
+
+ My proSpace
+
+
+ ),
+};
diff --git a/packages/components/src/components/LabeledValue/stories/EdgeCases.stories.tsx b/packages/components/src/components/LabeledValue/stories/EdgeCases.stories.tsx
new file mode 100644
index 000000000..1d4f7dfd0
--- /dev/null
+++ b/packages/components/src/components/LabeledValue/stories/EdgeCases.stories.tsx
@@ -0,0 +1,35 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import LabeledValue from "../LabeledValue";
+import defaultMeta from "./Default.stories";
+import { dummyText } from "@/lib/dev/dummyText";
+import { Label } from "@/components/Label";
+import { Content } from "@/components/Content";
+import React from "react";
+import { CopyButton } from "@/components/CopyButton";
+
+const meta: Meta = {
+ title: "Content/Labeled Value/Edge Cases",
+ ...defaultMeta,
+};
+export default meta;
+
+type Story = StoryObj;
+
+export const LongLabel: Story = {
+ render: (props) => (
+
+
+ {dummyText.short}
+
+
+ ),
+};
+export const LongContent: Story = {
+ render: (props) => (
+
+
+ {dummyText.long}
+
+
+ ),
+};
diff --git a/packages/components/src/components/StatusIcon/StatusIcon.tsx b/packages/components/src/components/StatusIcon/StatusIcon.tsx
index 1ebd828c4..768904f7c 100644
--- a/packages/components/src/components/StatusIcon/StatusIcon.tsx
+++ b/packages/components/src/components/StatusIcon/StatusIcon.tsx
@@ -14,9 +14,9 @@ export interface StatusIconProps extends StatusVariantProps {
export const StatusIcon: FC = (props) => {
const { variant = "info", ...rest } = props;
- const ariaLabel = useLocalizedStringFormatter(locales).format(
- `statusIcon.${variant}`,
- );
+ const stringFormatter = useLocalizedStringFormatter(locales);
+
+ const ariaLabel = stringFormatter.format(`statusIcon.${variant}`);
const icon =
variant === "info"
diff --git a/packages/components/src/components/Tooltip/stories/Default.stories.tsx b/packages/components/src/components/Tooltip/stories/Default.stories.tsx
index bad41a996..6ed6d552a 100644
--- a/packages/components/src/components/Tooltip/stories/Default.stories.tsx
+++ b/packages/components/src/components/Tooltip/stories/Default.stories.tsx
@@ -4,17 +4,20 @@ import React from "react";
import { Text } from "@/components/Text";
import { Button } from "@/components/Button";
import { Icon } from "@/components/Icon";
-import { faCopy } from "@fortawesome/free-regular-svg-icons/faCopy";
+import { faSave } from "@fortawesome/free-regular-svg-icons/faSave";
const meta: Meta = {
title: "Overlays/Tooltip",
component: Tooltip,
- render: () => (
-
-