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

feat(APIKeyModal): add typescript types #4336

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright IBM Corp. 2021, 2021
// Copyright IBM Corp. 2021, 2024
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
Expand Down Expand Up @@ -27,16 +27,17 @@ import { usePortalTarget } from '../../global/js/hooks/usePortalTarget';
import { getDevtoolsProps } from '../../global/js/utils/devtools';
import { isRequiredIf } from '../../global/js/utils/props-helper';
import uuidv4 from '../../global/js/utils/uuidv4';
import { APIKeyModalProps } from './APIKeyModal.types';

const componentName = 'APIKeyModal';

// Default values for props
const defaults = {
apiKeyName: '',
customSteps: Object.freeze([]),
customSteps: [],
};

export let APIKeyModal = forwardRef(
export let APIKeyModal: React.FC<APIKeyModalProps> = forwardRef(
(
{
// The component props, in alphabetical order (for consistency).
Expand Down Expand Up @@ -87,13 +88,13 @@ export let APIKeyModal = forwardRef(
// Collect any other property values passed in.
...rest
},
ref
ref: React.Ref<HTMLDivElement>
) => {
const [title, setTitle] = useState(null);
const [title, setTitle] = useState<string | null | undefined>(null);
const [copyError, setCopyError] = useState(false);
const [name, setName] = useState(apiKeyName);
const [currentStep, setCurrentStep] = useState(0);
const copyRef = useRef();
const copyRef = useRef<HTMLButtonElement>();
const apiKeyInputId = useRef(uuidv4());
const nameInputId = useRef(uuidv4());
const renderPortalUse = usePortalTarget(portalTargetIn);
Expand All @@ -118,8 +119,8 @@ export let APIKeyModal = forwardRef(
if (loading) {
return true;
}
if (hasSteps && 'valid' in customSteps[currentStep]) {
return !customSteps[currentStep].valid;
if (hasSteps && 'valid' in (customSteps?.[currentStep] || [])) {
return !customSteps[currentStep]?.valid;
}
if (!hasSteps && nameRequired && !name) {
return true;
Expand Down Expand Up @@ -153,7 +154,7 @@ export let APIKeyModal = forwardRef(
} else if (apiKeyLoaded) {
setTitle(generateSuccessTitle);
} else if (hasSteps) {
setTitle(customSteps[currentStep].title);
setTitle(customSteps[currentStep]?.title);
} else {
setTitle(generateTitle);
}
Expand All @@ -176,7 +177,7 @@ export let APIKeyModal = forwardRef(
const onCloseHandler = () => {
setName('');
setCurrentStep(0);
onClose();
onClose?.();
};

const submitHandler = async (e) => {
Expand All @@ -195,9 +196,9 @@ export let APIKeyModal = forwardRef(
}
}
} else if (editing) {
onRequestEdit(name);
onRequestEdit?.(name);
} else {
onRequestGenerate(name);
onRequestGenerate?.(name);
}
};

Expand Down Expand Up @@ -226,7 +227,7 @@ export let APIKeyModal = forwardRef(
/>
<ModalBody className={`${blockClass}__body-container`}>
{hasSteps && !apiKeyLoaded ? (
customSteps[currentStep].content
customSteps[currentStep]?.content
) : (
<>
{body && <p className={`${blockClass}__body`}>{body}</p>}
Expand All @@ -248,7 +249,7 @@ export let APIKeyModal = forwardRef(
/>
)}
{(editing || (!apiKeyLoaded && nameRequired)) && (
<Form onSubmit={submitHandler} aria-label={title}>
<Form onSubmit={submitHandler} aria-label={title ?? undefined}>
<TextInput
helperText={nameHelperText}
placeholder={namePlaceholder}
Expand Down Expand Up @@ -373,6 +374,7 @@ APIKeyModal.propTypes = {
/**
* if you need more options for key creation beyond just the name use custom steps to obtain whatever data is required.
*/
/**@ts-ignore*/
customSteps: PropTypes.arrayOf(
PropTypes.shape({
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { ReactNode } from 'react';

interface APIKeyModalCommonProps {
/**
* the api key that's displayed to the user when a request to create is fulfilled.
*/
apiKey?: string;
/**
* label for the text input that holds the api key.
*/
apiKeyLabel?: string;
/**
* the name of the api key. should only be supplied in edit mode.
*/
apiKeyName?: string;
/**
* body content for the modal
*/
body?: string;
/**
* optional class name
*/
className?: string;
/**
* text for the close button
*/
closeButtonText?: string;
/**
* text for the copy button
*/
copyButtonText?: string;
/**
* Error message for when the copy function fails
*/
copyErrorText?: string;
/**
* text description for the copy button icon
*/
copyIconDescription?: string;
/**
* if you need more options for key creation beyond just the name use custom steps to obtain whatever data is required.
*/
customSteps?: Array<{
valid?: boolean;
content?: ReactNode;
title?: string;
}>;
/**
* designates if the modal is in the edit mode
*/
editing?: boolean;
/**
* designates if an error has occurred in a request
*/
error?: boolean;
/**
* text to display if an error has occurred
*/
errorText?: string;
/**
* default primary button text for modal in assumed default mode create or generate.
* in create mode this is the button text prior to supplying an api key, which then
* uses copyButtonText
*/
generateButtonText?: string;
/**
* content to display if generate request was successful
*/
generateSuccessBody?: ReactNode;
/**
* title for a successful key generation
*/
generateSuccessTitle?: string;
/**
* default title for the modal in generate key mode
*/
generateTitle?: string;
/**
* designates if the api input has the visibility toggle enabled
*/
hasAPIKeyVisibilityToggle?: boolean;
/**
* designates if user is able to download the api key
*/
hasDownloadLink?: boolean;
/**
* label text that's displayed when hovering over visibility toggler to hide key
*/
hideAPIKeyLabel?: string;
/**
* designates if the modal is in a loading state via a request or some other in progress operation
*/
loading?: boolean;
/**
* text that displays while modal is in the loading state
*/
loadingText?: string;
/**
* general label text for modal
*/
modalLabel?: string;
/**
* helper text for name input
*/
nameHelperText?: string;
/**
* label for api key name input
*/
nameLabel?: string;
/**
* placeholder text for api key name input
*/
namePlaceholder?: string;
/**
* designates if a name is required or not for key generation. NOTE- if using custom steps set this to false since you will be using your own validation
*/
nameRequired?: boolean;
/**
* handler for on modal close
*/
onClose?(): void;
/**
* Optional callback if you want to use your own copy function instead of the build in one
* onCopy(apiKey)
*/
onCopy?(apiKey: string): void;
/**
* handler for api key edit
*/
onRequestEdit?(apiKeyName: string): void;
/**
* handler for api key generation
*/
onRequestGenerate?(apiKeyName: string): void;
/**
* designates if modal is open or closed
*/
open: boolean;
/**
* The DOM node the tearsheet should be rendered within. Defaults to document.body.
*/
portalTarget: ReactNode;
/**
* label text that's displayed when hovering over visibility toggler to show key
*/
showAPIKeyLabel?: string;
}

type CustomStepConditionalProps = {
/**
* if you need more options for key creation beyond just the name use custom steps to obtain whatever data is required.
*/
customSteps?: Array<{
valid?: boolean;
content?: ReactNode;
title?: string;
}>;
/**
* text that displays in the secondary button when using custom steps to indicate to the user that there is a previous step
*/
previousStepButtonText: string;
/**
* text that displays in the primary button when using custom steps to indicate to the user that there is a next step
*/
nextStepButtonText: string;
};

type EditingConditionalProps = {
/**
* designates if the modal is in the edit mode
*/
editing?: boolean;
/**
* text for the edit button
*/
editButtonText: string;
/**
* designates if the edit request was successful
*/
editSuccess: boolean;
/**
* title for a successful edit
*/
editSuccessTitle: string;
};

type HasDownloadLinkProps = {
/**
* designates if user is able to download the api key
*/
hasDownloadLink?: boolean;
/**
* the content that appears that indicates the key is downloadable
*/
downloadBodyText: string;
/**
* designates the name of downloadable json file with the key. if not specified will default to 'apikey'
*/
downloadFileName: string;
/**
* designates the file type for the downloadable key
*/
downloadFileType: 'txt' | 'json';
/**
* anchor text for the download link
*/
downloadLinkText: string;
};

export type APIKeyModalProps = APIKeyModalCommonProps &
CustomStepConditionalProps &
EditingConditionalProps &
HasDownloadLinkProps;
Loading
Loading