diff --git a/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx b/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx index 453a5ad4556..1e67e4c3b00 100644 --- a/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx +++ b/packages/react-core/src/components/ClipboardCopy/ClipboardCopy.tsx @@ -55,6 +55,10 @@ export interface ClipboardCopyProps extends Omit textAriaLabel?: string; /** Aria-label to use on the ClipboardCopyToggle. */ toggleAriaLabel?: string; + /** ID to use on the TextInput. */ + inputId?: string; + /** Name attribute to use on the TextInput. */ + inputName?: string; /** Flag to show if the input is read only. */ isReadOnly?: boolean; /** Flag to determine if clipboard copy is in the expanded state initially */ @@ -91,6 +95,10 @@ export interface ClipboardCopyProps extends Omit onCopy?: (event: React.ClipboardEvent, text?: React.ReactNode) => void; /** A function that is triggered on changing the text. */ onChange?: (event: React.FormEvent, text?: string) => void; + /** Callback function when text input is focused */ + onInputFocus?: (event?: any) => void; + /** Callback function when text input is blurred (focus leaves) */ + onInputBlur?: (event?: any) => void; /** The text which is copied. */ children: string | string[]; /** Additional actions for inline clipboard copy. Should be wrapped with ClipboardCopyAction. */ @@ -177,6 +185,8 @@ class ClipboardCopy extends Component { /* eslint-disable @typescript-eslint/no-unused-vars */ isExpanded, onChange, // Don't pass to
+ onInputFocus, // Don't pass to
+ onInputBlur, // Don't pass to
/* eslint-enable @typescript-eslint/no-unused-vars */ isReadOnly, isCode, @@ -189,6 +199,8 @@ class ClipboardCopy extends Component { clickTip, textAriaLabel, toggleAriaLabel, + inputId, + inputName, variant, position, className, @@ -295,8 +307,11 @@ class ClipboardCopy extends Component { readOnlyVariant={isReadOnly || this.state.expanded ? 'default' : undefined} onChange={this.updateText} value={this.state.expanded ? this.state.textWhenExpanded : copyableText} - id={`text-input-${id}`} + id={inputId ?? `text-input-${id}`} + name={inputName} aria-label={textAriaLabel} + onFocus={onInputFocus} + onBlur={onInputBlur} {...(isCode && { dir: 'ltr' })} /> { expect(screen.getByRole('textbox')).toHaveAccessibleName('text label'); }); +test('Passes inputId to TextInput', () => { + render({children}); + + expect(screen.getByRole('textbox')).toHaveAttribute('id', 'custom-input-id'); +}); + +test('Passes inputName to TextInput', () => { + render({children}); + + expect(screen.getByRole('textbox')).toHaveAttribute('name', 'custom-input-name'); +}); + test('Calls onChange when ClipboardCopy textinput is typed in', async () => { const onChangeMock = jest.fn(); const user = userEvent.setup(); @@ -338,6 +350,66 @@ test('Does not call onChange when ClipboardCopy textinput is not typed in', asyn expect(onChangeMock).not.toHaveBeenCalled(); }); +test('Calls onFocus when ClipboardCopy textinput is focused', async () => { + const onFocusMock = jest.fn(); + const user = userEvent.setup(); + + render({children}); + + await user.click(screen.getByRole('textbox')); + + expect(onFocusMock).toHaveBeenCalledTimes(1); +}); + +test('Does not call onFocus when ClipboardCopy textinput is not focused', async () => { + const onFocusMock = jest.fn(); + const user = userEvent.setup(); + + render( + <> + {children} + + + ); + + await user.click(screen.getByRole('textbox', { name: 'native input' })); + + expect(onFocusMock).not.toHaveBeenCalled(); +}); + +test('Calls onBlur when ClipboardCopy textinput loses focus', async () => { + const onBlurMock = jest.fn(); + const user = userEvent.setup(); + + render( + <> + {children} + + + ); + + await user.click(screen.getByRole('textbox', { name: 'Copyable input' })); + await user.click(screen.getByRole('textbox', { name: 'native input' })); + + expect(onBlurMock).toHaveBeenCalledTimes(1); +}); + +test('Does not call onBlur when ClipboardCopy textinput does not lose focus', async () => { + const onBlurMock = jest.fn(); + const user = userEvent.setup(); + + render( + <> + {children} + + + ); + + await user.click(screen.getByRole('textbox', { name: 'native input' })); + + expect(onBlurMock).not.toHaveBeenCalled(); +}); + test('Calls onCopy when ClipboardCopyButton is clicked', async () => { const onCopyMock = jest.fn(); const user = userEvent.setup(); diff --git a/packages/react-core/src/components/ClipboardCopy/__tests__/__snapshots__/ClipboardCopy.test.tsx.snap b/packages/react-core/src/components/ClipboardCopy/__tests__/__snapshots__/ClipboardCopy.test.tsx.snap index ca7c7931a23..e936ecf1237 100644 --- a/packages/react-core/src/components/ClipboardCopy/__tests__/__snapshots__/ClipboardCopy.test.tsx.snap +++ b/packages/react-core/src/components/ClipboardCopy/__tests__/__snapshots__/ClipboardCopy.test.tsx.snap @@ -18,7 +18,7 @@ exports[`Matches snapshot 1`] = `