Skip to content

Commit

Permalink
refactor(Checklist): add typescript types (carbon-design-system#5055)
Browse files Browse the repository at this point in the history
* refactor(checklist): add typescript types

* chore(checklist): copyright year updated

* chore(checklist): type added for enableToggle prop

* fix(checklist): title type updated and removed showToggle
  • Loading branch information
szinta authored May 15, 2024
1 parent 2bd1af9 commit 26818a5
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,26 @@ export default {
component: Checklist,
tags: ['autodocs'],
argTypes: {
taskLists: {
table: {
type: {
detail: `[{
title: string,
tasks: [{
kind: 'unchecked' | 'indeterminate' | 'checked' | 'disabled' | 'error',
label: string,
onClick: func,
}]
}]`,
},
},
},
theme: {
control: { type: null },
table: {
defaultValue: { summary: 'light' },
type: { summary: "'light' | 'dark'" },
},
},
},
parameters: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 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 All @@ -13,7 +13,13 @@
*/

// Import portions of React that are needed.
import React, { useEffect, useRef, useState } from 'react';
import React, {
ForwardedRef,
ReactNode,
useEffect,
useRef,
useState,
} from 'react';

// Other standard imports.
import PropTypes from 'prop-types';
Expand All @@ -29,6 +35,7 @@ import { ChevronUp } from '@carbon/react/icons';

import { ChecklistIcon } from './ChecklistIcon';
import { ChecklistChart } from './ChecklistChart';
import { Kinds, Kind, Theme, Themes } from './Checklist.types';

// The block part of our conventional BEM class names (blockClass__E--M).
const blockClass = `${pkg.prefix}--checklist`;
Expand All @@ -45,6 +52,91 @@ const componentName = 'Checklist';
// Default values should be provided when the component needs to make a choice
// or assumption when a prop is not supplied.

type Task = {
kind: Kind;
label: string;
onClick?(task?: Task): void;
};

type TaskList = {
title?: string;
tasks: Array<Task>;
};

interface ChecklistProps {
/**
* Define both `chartLabel` and `chartValue` to show the chart and its label together.
*/
chartLabel?: string;
/**
* A number between 0 and 1.
*
* Define both `chartLabel` and `chartValue` to show the chart and its label together.
*/
chartValue?: number;
/**
* Aria-label for the Checklist component.
*/
checklistAriaLabel?: string;
/**
* Aria-label for the Checklist's toggle component.
*/
checklistToggleAriaLabel?: string;
/**
* Provide an optional class to be applied to the containing node.
*/
className?: string;
/**
* Whether or not to show the open/close toggle.
*/
enableToggle?: boolean;
/**
* Callback for the "View all" button. See also `viewAllLabel`.
*/
onClickViewAll?(): void;
/**
* Optional callback for when the list is opened/closed.
*/
onToggle?(isOpen?: boolean): void;
/**
* Specifies whether the component is opened or closed.
* This can be set during initialization, or changed after being rendered.
*/
open?: boolean;
/**
* The task list can be broken down into sub-lists.
*
* Each sub-list can include an optional `title`.
*
* Each task must specify the appropriate icon (`kind`) and `label`.
*
* If any task has an `onClick` callback function defined,
* then the `label` will be rendered as a button,
* else the `label` will be rendered as plain text.
*/
taskLists: Array<TaskList>;
/**
* Determines the theme of the component.
*/
theme?: Theme;
/**
* The title of the component.
*/
title?: ReactNode;
/**
* The label for the toggle's tooltip.
*/
toggleLabel?: string;
/**
* The alignment of the toggle's tooltip.
*/
toggleLabelAlign?: React.ComponentProps<typeof IconButton>['align'];
/**
* If defined, will show and enable the "View all (#)" button at the bottom of the list.
*/
viewAllLabel?: string;
}

// Default values for props
const defaults = {
checklistAriaLabel: 'Checklist',
Expand All @@ -54,7 +146,7 @@ const defaults = {
open: true,
enableToggle: true,
taskLists: [],
theme: 'light',
theme: Themes.light,
toggleLabel: 'Toggle',
toggleLabelAlign: 'top',
};
Expand Down Expand Up @@ -84,8 +176,8 @@ export let Checklist = React.forwardRef(
toggleLabelAlign = defaults.toggleLabelAlign,
viewAllLabel,
...rest
},
ref
}: ChecklistProps,
ref: ForwardedRef<HTMLElement>
) => {
const [isOpen, setIsOpen] = useState(open);
const listContainerId = useRef(uuidv4()).current;
Expand Down Expand Up @@ -196,7 +288,7 @@ export let Checklist = React.forwardRef(
className={`${blockClass}__list-item`}
key={`${item.label}-${index}`}
>
<ChecklistIcon kind={item.kind} />
<ChecklistIcon kind={item.kind} theme={theme} />

{typeof item.onClick === 'function' ? (
<Button
Expand All @@ -205,7 +297,7 @@ export let Checklist = React.forwardRef(
item.kind === 'error',
})}
onClick={() => {
item.onClick(item);
item.onClick?.(item);
}}
size="sm"
title={item.label}
Expand Down Expand Up @@ -311,17 +403,18 @@ Checklist.propTypes = {
* then the `label` will be rendered as a button,
* else the `label` will be rendered as plain text.
*/
/**@ts-ignore */
taskLists: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string,
tasks: PropTypes.arrayOf(
PropTypes.shape({
kind: PropTypes.oneOf([
'unchecked',
'indeterminate',
'checked',
'disabled',
'error',
Kinds.unchecked,
Kinds.indeterminate,
Kinds.checked,
Kinds.disabled,
Kinds.error,
]).isRequired,
label: PropTypes.string.isRequired,
onClick: PropTypes.func,
Expand All @@ -332,7 +425,7 @@ Checklist.propTypes = {
/**
* Determines the theme of the component.
*/
theme: PropTypes.oneOf(['light', 'dark']),
theme: PropTypes.oneOf([Themes.light, Themes.dark]),
/**
* The title of the component.
*/
Expand Down
28 changes: 28 additions & 0 deletions packages/ibm-products/src/components/Checklist/Checklist.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright IBM Corp. 2024, 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.
*/

export enum Themes {
light = 'light',
dark = 'dark',
}

export type Theme = Themes.light | Themes.dark;

export enum Kinds {
unchecked = 'unchecked',
indeterminate = 'indeterminate',
checked = 'checked',
disabled = 'disabled',
error = 'error',
}

export type Kind =
| Kinds.unchecked
| Kinds.indeterminate
| Kinds.checked
| Kinds.disabled
| Kinds.error;
11 changes: 8 additions & 3 deletions packages/ibm-products/src/components/Checklist/ChecklistChart.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @ts-check
/**
* Copyright IBM Corp. 2023, 2024
*
Expand All @@ -13,21 +14,25 @@ import { purple50, gray20, gray70 } from '@carbon/colors';

import { getDevtoolsProps } from '../../global/js/utils/devtools';
import { pkg } from '../../settings';
import { Themes } from './Checklist.types';

const blockClass = `${pkg.prefix}--checklist__chart`;
const componentName = 'ChecklistChart';

const defaults = {
theme: 'light',
theme: Themes.light,
};

/**
* Custom chart component used within Checklist PLG component
*/
export let ChecklistChart = React.forwardRef(
/**
* @param {{className?: string, value: number, theme?: import('./Checklist.types').Theme}} props type description
*/
({ className, value, theme = defaults.theme, ...rest }, ref) => {
const numDegrees = clamp(value * 360, 0, 360);
const circleColor = theme === 'light' ? gray20 : gray70; // $ui-03 (-ish)
const circleColor = theme === Themes.light ? gray20 : gray70; // $ui-03 (-ish)
const progressColor = purple50;

return (
Expand Down Expand Up @@ -59,7 +64,7 @@ ChecklistChart.propTypes = {
/**
* Determines the theme of the component.
*/
theme: PropTypes.oneOf(['light', 'dark']),
theme: PropTypes.oneOf([Themes.light, Themes.dark]),
/**
* Number between 0 and 1.
*/
Expand Down
21 changes: 13 additions & 8 deletions packages/ibm-products/src/components/Checklist/ChecklistIcon.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check
/**
* Copyright IBM Corp. 2023, 2023
* Copyright IBM Corp. 2023, 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 @@ -30,6 +31,7 @@ import {
Incomplete,
Warning,
} from '@carbon/react/icons';
import { Kinds, Themes } from './Checklist.types';

// The block part of our conventional BEM class names (blockClass__E--M).
const blockClass = `${pkg.prefix}--checklist__icon`;
Expand All @@ -48,13 +50,16 @@ const componentName = 'ChecklistIcon';

// Default values for props
const defaults = {
theme: 'light',
theme: Themes.light,
};

/**
* TODO: A description of the component.
*/
export let ChecklistIcon = React.forwardRef(
/**
* @param {{className?: string, kind?: import('./Checklist.types').Kind, theme?: import('./Checklist.types').Theme}} props type description
*/
({ className, kind, theme = defaults.theme, ...rest }, ref) => {
let Icon;

Expand Down Expand Up @@ -112,14 +117,14 @@ ChecklistIcon.propTypes = {
* The icon to be displayed.
*/
kind: PropTypes.oneOf([
'unchecked',
'indeterminate',
'checked',
'disabled',
'error',
Kinds.unchecked,
Kinds.indeterminate,
Kinds.checked,
Kinds.disabled,
Kinds.error,
]),
/**
* Determines the theme of the component.
*/
theme: PropTypes.oneOf(['light', 'dark']),
theme: PropTypes.oneOf([Themes.light, Themes.dark]),
};

0 comments on commit 26818a5

Please sign in to comment.