Skip to content

Commit

Permalink
feat: add contained-list component (#11969)
Browse files Browse the repository at this point in the history
* feat(contained-list): scaffold new component

* feat(contained-list-item): scaffold new component

* feat(contained-list-item): add support for clickable items

* feat(contained-list): add support for disabled list items

* feat(contained-list): add support for actions in header and item

* docs(contained-list): build stories

* feat(contained-list-item): add support for icons

* test(contained-list): add tests

* refactor(contained-list): rename variants

* docs(contained-list): change "Heading" to "List title"

* fix(contained-list-item): add extra padding when action is pased

* docs(contained-list): change "Heading" to "List title"

Co-authored-by: Lauren Rice <43969356+laurenmrice@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Oct 6, 2022
1 parent 2cb3054 commit e346d01
Show file tree
Hide file tree
Showing 12 changed files with 900 additions and 0 deletions.
100 changes: 100 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Expand Up @@ -8639,6 +8639,106 @@ Map {
"$$typeof": Symbol(react.forward_ref),
"render": [Function],
},
"unstable_ContainedList" => Object {
"ContainedListItem": Object {
"propTypes": Object {
"action": Object {
"type": "node",
},
"children": Object {
"type": "node",
},
"className": Object {
"type": "string",
},
"disabled": Object {
"type": "bool",
},
"onClick": Object {
"type": "func",
},
"renderIcon": Object {
"args": Array [
Array [
Object {
"type": "func",
},
Object {
"type": "object",
},
],
],
"type": "oneOfType",
},
},
},
"propTypes": Object {
"action": Object {
"type": "node",
},
"children": Object {
"type": "node",
},
"className": Object {
"type": "string",
},
"kind": Object {
"args": Array [
Array [
"on-page",
"disclosed",
],
],
"type": "oneOf",
},
"label": Object {
"args": Array [
Array [
Object {
"type": "string",
},
Object {
"type": "node",
},
],
],
"isRequired": true,
"type": "oneOfType",
},
},
},
"unstable_ContainedListItem" => Object {
"propTypes": Object {
"action": Object {
"type": "node",
},
"children": Object {
"type": "node",
},
"className": Object {
"type": "string",
},
"disabled": Object {
"type": "bool",
},
"onClick": Object {
"type": "func",
},
"renderIcon": Object {
"args": Array [
Array [
Object {
"type": "func",
},
Object {
"type": "object",
},
],
],
"type": "oneOfType",
},
},
},
"unstable_FeatureFlags" => Object {
"propTypes": Object {
"children": Object {
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/__tests__/index-test.js
Expand Up @@ -214,6 +214,8 @@ describe('Carbon Components React', () => {
"TreeView",
"UnorderedList",
"VStack",
"unstable_ContainedList",
"unstable_ContainedListItem",
"unstable_FeatureFlags",
"unstable_LayoutDirection",
"unstable_Menu",
Expand Down
74 changes: 74 additions & 0 deletions packages/react/src/components/ContainedList/ContainedList.js
@@ -0,0 +1,74 @@
/**
* Copyright IBM Corp. 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useId } from '../../internal/useId';
import { usePrefix } from '../../internal/usePrefix';

const variants = ['on-page', 'disclosed'];

function ContainedList({
action,
children,
className,
kind = variants[0],
label,
}) {
const labelId = `${useId('contained-list')}-header`;
const prefix = usePrefix();

const classes = classNames(
`${prefix}--contained-list`,
`${prefix}--contained-list--${kind}`,
className
);

return (
<div className={classes}>
<div className={`${prefix}--contained-list__header`}>
<div id={labelId} className={`${prefix}--contained-list__label`}>
{label}
</div>
{action && (
<div className={`${prefix}--contained-list__action`}>{action}</div>
)}
</div>
<ul aria-labelledby={labelId}>{children}</ul>
</div>
);
}

ContainedList.propTypes = {
/**
* A slot for a possible interactive element to render.
*/
action: PropTypes.node,

/**
* A collection of ContainedListItems to be rendered in the ContainedList
*/
children: PropTypes.node,

/**
* Additional CSS class names.
*/
className: PropTypes.string,

/**
* The kind of ContainedList you want to display
*/
kind: PropTypes.oneOf(variants),

/**
* A label describing the contained list.
*/
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
};

export default ContainedList;
@@ -0,0 +1,96 @@
/**
* Copyright IBM Corp. 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { usePrefix } from '../../../internal/usePrefix';

function ContainedListItem({
action,
children,
className,
disabled = false,
onClick,
renderIcon: IconElement,
}) {
const prefix = usePrefix();

const isClickable = onClick !== undefined;

const classes = classNames(`${prefix}--contained-list-item`, className, {
[`${prefix}--contained-list-item--clickable`]: isClickable,
[`${prefix}--contained-list-item--with-icon`]: IconElement,
[`${prefix}--contained-list-item--with-action`]: action,
});

const content = (
<>
{IconElement && (
<div className={`${prefix}--contained-list-item__icon`}>
<IconElement />
</div>
)}
<div>{children}</div>
</>
);

return (
<li className={classes}>
{isClickable ? (
<button
className={`${prefix}--contained-list-item__content`}
type="button"
disabled={disabled}
onClick={onClick}>
{content}
</button>
) : (
<div className={`${prefix}--contained-list-item__content`}>
{content}
</div>
)}
{action && (
<div className={`${prefix}--contained-list-item__action`}>{action}</div>
)}
</li>
);
}

ContainedListItem.propTypes = {
/**
* A slot for a possible interactive element to render within the item.
*/
action: PropTypes.node,

/**
* The content of this item. Must not contain any interactive elements. Use props.action to include those.
*/
children: PropTypes.node,

/**
* Additional CSS class names.
*/
className: PropTypes.string,

/**
* Whether this item is disabled.
*/
disabled: PropTypes.bool,

/**
* Provide an optional function to be called when the item is clicked.
*/
onClick: PropTypes.func,

/**
* Provide an optional icon to render in front of the item's content.
*/
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
};

export default ContainedListItem;
@@ -0,0 +1,8 @@
/**
* Copyright IBM Corp. 2022
*
* 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 default from './ContainedListItem';

0 comments on commit e346d01

Please sign in to comment.