Skip to content

Commit

Permalink
Clipboard & Copy Button - Fix handling of empty string and zero v…
Browse files Browse the repository at this point in the history
…alues (HDS-4447) (#2685)

Co-authored-by: Cristiano Rastelli <[email protected]>
  • Loading branch information
KristinLBradley and didoo authored Feb 7, 2025
1 parent 47a9476 commit 0361b9b
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 48 deletions.
7 changes: 7 additions & 0 deletions .changeset/early-knives-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@hashicorp/design-system-components": patch
---

`Copy Button` - Fixed issue preventing copying of empty string and zero number values.

`Copy Snippet` - Fixed issue preventing copying of empty string and zero number values.
85 changes: 40 additions & 45 deletions packages/components/src/modifiers/hds-clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,20 @@ export interface HdsClipboardModifierSignature {
export const getTextToCopy = (text: TextToCopy): string => {
let textToCopy: string = '';

if (text) {
if (typeof text === 'string') {
textToCopy = text;
} else if (
// context: https://github.com/hashicorp/design-system/pull/1564
typeof text === 'number' ||
typeof text === 'bigint'
) {
textToCopy = text.toString();
} else {
assert(
`\`hds-clipboard\` modifier - \`text\` argument must be a string - provided: ${typeof text}`
);
}
if (typeof text === 'string') {
textToCopy = text;
} else if (
// context: https://github.com/hashicorp/design-system/pull/1564
typeof text === 'number' ||
typeof text === 'bigint'
) {
textToCopy = text.toString();
} else {
assert(
`\`hds-clipboard\` modifier - \`text\` argument must be a string or number - provided: ${typeof text}`
);
}

return textToCopy;
};

Expand Down Expand Up @@ -109,38 +108,34 @@ export const writeTextToClipboard = async (
): Promise<boolean> => {
// finally copy the text to the clipboard using the Clipboard API
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API
if (textToCopy) {
try {
// notice: the "clipboard-write" permission is granted automatically to pages when they are in the active tab
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write
await navigator.clipboard.writeText(textToCopy);
// DEBUG uncomment this for easy debugging
// console.log('success', textToCopy);
return true;
} catch {
// if it is not a secure context, use the polyfill
// to test that this works in a non-secure context, access the port through your IP address (ie. XXX.XXX.X.XXX:4200/)
if (!navigator.clipboard) {
try {
const clipboard = await import('clipboard-polyfill');
await clipboard.writeText(textToCopy);
return true;
} catch (error) {
warn(
`copy action failed, unable to use clipboard-polyfill: ${JSON.stringify(
error
)}`,
{
id: 'hds-clipboard.write-text-to-clipboard.catch-error',
}
);
return false;
}
try {
// notice: the "clipboard-write" permission is granted automatically to pages when they are in the active tab
// https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write
await navigator.clipboard.writeText(textToCopy);
// DEBUG uncomment this for easy debugging
// console.log('success', textToCopy);
return true;
} catch {
// if it is not a secure context, use the polyfill
// to test that this works in a non-secure context, access the port through your IP address (ie. XXX.XXX.X.XXX:4200/)
if (!navigator.clipboard) {
try {
const clipboard = await import('clipboard-polyfill');
await clipboard.writeText(textToCopy);
return true;
} catch (error) {
warn(
`copy action failed, unable to use clipboard-polyfill: ${JSON.stringify(
error
)}`,
{
id: 'hds-clipboard.write-text-to-clipboard.catch-error',
}
);
return false;
}

return false;
}
} else {

return false;
}
};
Expand All @@ -151,7 +146,7 @@ export const copyToClipboard = async (
): Promise<boolean> => {
let textToCopy: string = '';

if (text) {
if (text !== undefined) {
textToCopy = getTextToCopy(text);
} else if (target) {
const targetElement = getTargetElement(target);
Expand Down
11 changes: 11 additions & 0 deletions showcase/app/templates/components/copy/button.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@
</SF.Item>
</Shw::Flex>

<Shw::Text::H4>Special cases</Shw::Text::H4>

<Shw::Flex as |SF|>
<SF.Item>
<Hds::Copy::Button @text="Copy an empty string" @textToCopy="" />
</SF.Item>
<SF.Item>
<Hds::Copy::Button @text="Copy the number '0'" @textToCopy={{0}} />
</SF.Item>
</Shw::Flex>

<Shw::Divider @level={{2}} />

<Shw::Text::H3>With <code>target</code> element</Shw::Text::H3>
Expand Down
6 changes: 6 additions & 0 deletions showcase/app/templates/components/copy/snippet.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@
/>
</Shw::Outliner>
</SF.Item>
<SF.Item @label="With an empty string to copy">
<Hds::Copy::Snippet @textToCopy="" />
</SF.Item>
</Shw::Flex>

<Shw::Flex as |SF|>
<SF.Item @label="With number to copy">
{{! context: https://github.com/hashicorp/design-system/pull/1564 }}
<Hds::Copy::Snippet @textToCopy={{123456789}} />
</SF.Item>
<SF.Item @label="With the number '0' to copy">
<Hds::Copy::Snippet @textToCopy={{0}} />
</SF.Item>
</Shw::Flex>

<Shw::Text::H2>Full width</Shw::Text::H2>
Expand Down
27 changes: 24 additions & 3 deletions showcase/tests/integration/modifiers/hds-clipboard-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,6 @@ module(
const success = await writeTextToClipboard('test');
assert.false(success);
});
test('returns `false` as response if no `textToCopy` argument is provided', async function (assert) {
assert.false(await writeTextToClipboard());
});
}
);

Expand Down Expand Up @@ -265,6 +262,18 @@ module('Integration | Modifier | hds-clipboard', function (hooks) {
assert.true(this.success);
});

test('it should copy an empty string provided as a `@text` argument', async function (assert) {
await render(
hbs`<button id="test-button" {{hds-clipboard
text=""
onSuccess=this.onSuccess
onError=this.onError
}}>Test</button>`
);
await click('button#test-button');
assert.true(this.success);
});

// context: https://github.com/hashicorp/design-system/pull/1564
test('it should allow to copy an `integer` provided as `@text` argument', async function (assert) {
await render(
Expand All @@ -278,6 +287,18 @@ module('Integration | Modifier | hds-clipboard', function (hooks) {
assert.true(this.success);
});

test('it should copy a zero number value provided as a `@text` argument', async function (assert) {
await render(
hbs`<button id="test-button" {{hds-clipboard
text=0
onSuccess=this.onSuccess
onError=this.onError
}}>Test</button>`
);
await click('button#test-button');
assert.true(this.success);
});

// @TARGET ARGUMENT

test('it should allow to target an element using a `string` selector for the `@target` argument', async function (assert) {
Expand Down

0 comments on commit 0361b9b

Please sign in to comment.