Skip to content

Commit

Permalink
PD-12487 Make CollapsibleList component
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiek-acl committed Jul 15, 2019
1 parent a6d1b4b commit b75fd0a
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 76 deletions.
File renamed without changes.
File renamed without changes.
37 changes: 37 additions & 0 deletions packages/CollapsibleChecklists/src/CollapsibleChecklists.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import PropTypes from "prop-types";
// import CollapsibleChecklistsStyles from "./CollapsibleChecklists.styles";
import Heading from "./components/Heading";
import Group from "./components/Group";
import Item from "./components/Item";

const propTypes = {
children: PropTypes.node,
};

const defaultProps = {};

const CollapsibleChecklists = props => {
const { children, onChange } = props;

return children.map((child, index) => {
switch (child.type.displayName) {
case Heading.displayName:
return <Heading key={`heading${index}`} {...child.props} />; // eslint-disable-line react/no-array-index-key
case Group.displayName:
return <Group key={`group${index}`} {...child.props} onChange={onChange} />; // eslint-disable-line react/no-array-index-key
default:
return child;
}
});
};

CollapsibleChecklists.displayName = "CollapsibleChecklists";
CollapsibleChecklists.propTypes = propTypes;
CollapsibleChecklists.defaultProps = defaultProps;

CollapsibleChecklists.Heading = Heading;
CollapsibleChecklists.Group = Group;
CollapsibleChecklists.Item = Item;

export default CollapsibleChecklists;
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import Collapsible from "@paprika/collapsible";
import Item from "../Item";

const propTypes = {
children: PropTypes.arrayOf(PropTypes.node), // are probably "Items", but could be a Spinner or anything else
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]), // are probably an array of "Items", but could be a Spinner or anything else
isDisabled: PropTypes.bool,
onChange: PropTypes.func,
onExpand: PropTypes.func,
title: PropTypes.node.isRequired,
};

const defaultProps = {
children: [],
isDisabled: false,
// onChange: () => {},
// onExpand: () => {},
};

function useIsIndeterminate(checkboxRef) {
Expand All @@ -21,14 +25,15 @@ function useIsIndeterminate(checkboxRef) {
}, [checkboxRef.current.indeterminate]);
}

const Group = props => {
function Group(props) {
const { children, isDisabled, onChange, onExpand, title } = props;
const [isCollapsed, setIsCollapsed] = React.useState(true);
const checkboxRef = React.useRef({});
useIsIndeterminate(checkboxRef);
let allAreChecked = React.Children.count(props.children) > 0;
let noneAreChecked = true;

React.Children.forEach(props.children, child => {
React.Children.forEach(children, child => {
if (child.props.isChecked) {
noneAreChecked = false;
} else {
Expand All @@ -44,16 +49,16 @@ const Group = props => {

function handleOnChange() {
const allChildrenAreChecked =
props.children.filter(child => child.props.isChecked || child.props.isDisabled).length === props.children.length;
children.filter(child => child.props.isChecked || child.props.isDisabled).length === children.length;

let childrenToChange = [];
if (allChildrenAreChecked) {
childrenToChange = props.children.filter(child => !child.props.isDisabled);
childrenToChange = children.filter(child => !child.props.isDisabled);
} else {
childrenToChange = props.children.filter(child => !child.props.isChecked && !child.props.isDisabled);
childrenToChange = children.filter(child => !child.props.isChecked && !child.props.isDisabled);
}

props.onChange(childrenToChange);
onChange(childrenToChange);
}

const label = (
Expand All @@ -62,18 +67,18 @@ const Group = props => {
ref={checkboxRef}
checked={allAreChecked}
type="checkbox"
disabled={props.isDisabled}
disabled={isDisabled}
onChange={handleOnChange}
/>
{props.title}
{title}
</React.Fragment>
);

const modifiedChildren = React.Children.map(props.children, child => {
const modifiedChildren = React.Children.map(children, child => {
const newProps =
child.type.displayName === Item.displayName
? {
onChange: () => props.onChange([child]),
onChange: () => onChange([child]),
}
: null;
return React.cloneElement(child, newProps);
Expand All @@ -84,19 +89,19 @@ const Group = props => {
a11yText="umm..."
hasOnlyIconToggle
isCollapsed={isCollapsed}
isDisabled={props.isDisabled}
isDisabled={isDisabled}
label={label}
onClick={() => {
if (isCollapsed) {
props.onExpand();
if (isCollapsed && onExpand) {
onExpand();
}
setIsCollapsed(!isCollapsed);
}}
>
{modifiedChildren}
</Collapsible>
);
};
}

Group.displayName = "Group";
Group.propTypes = propTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,24 @@ const propTypes = {
children: PropTypes.node.isRequired,
isChecked: PropTypes.bool,
isDisabled: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onChange: PropTypes.func,
};

const defaultProps = {
isChecked: false,
isDisabled: false,
onChange: () => {},
};

const Item = props => {
function Item(props) {
const { children, isChecked, isDisabled, onChange } = props;

return (
<div style={{ marginLeft: "16px" }}>
<input type="checkbox" checked={isChecked} disabled={isDisabled} onChange={onChange} /> {children}
</div>
);
};
}

Item.displayName = "Item";
Item.propTypes = propTypes;
Expand Down
3 changes: 3 additions & 0 deletions packages/CollapsibleChecklists/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import CollapsibleChecklists from "./CollapsibleChecklists";

export { CollapsibleChecklists as default };
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { withKnobs } from "@storybook/addon-knobs";

import BasicExample from "./examples/Basic";

storiesOf("CollapsibleList", module)
storiesOf("CollapsibleChecklists", module)
.addDecorator(withKnobs)
.add("Basic", () => <BasicExample />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import React from "react";
// import styled from "styled-components";
import InfoIcon from "@paprika/icon/lib/InfoCircle";
import Spinner from "../../../Spinner/src/index";
import CollapsibleList from "../../src/index";
import CollapsibleChecklists from "../../src/index";

// TODO: Rename to CollapsibleChecklists
// TODO: ensure each Item has 'isChecked', isDisabled, and 'name'. They can provide more attributes for their own use, but the component won't use them (e.g. ids for figuring out if checked)
// TODO: style it
// TODO: i18n
Expand Down Expand Up @@ -82,13 +81,13 @@ const ExampleStory = () => {
};

function handleOnChange(changedItemsArray) {
console.log("handleOnChange");
const newSportsData = sportsData.slice(0);

newSportsData.forEach(newSportsDatum => {
newSportsDatum.sports.forEach(sport => {
sport.teams.forEach(team => {
const thisTeamWasChanged =
changedItemsArray.filter(changedItem => changedItem.props.id === team.name).length > 0;
const thisTeamWasChanged = changedItemsArray.filter(changedItem => changedItem.key === team.name).length > 0;

if (thisTeamWasChanged) {
team.isChecked = !team.isChecked; // eslint-disable-line
Expand All @@ -102,9 +101,9 @@ const ExampleStory = () => {

function renderTeams(teams) {
return teams.map(team => (
<CollapsibleList.Item id={team.name} isChecked={team.isChecked} isDisabled={team.isDisabled}>
<CollapsibleChecklists.Item key={team.name} isChecked={team.isChecked} isDisabled={team.isDisabled}>
{team.name}
</CollapsibleList.Item>
</CollapsibleChecklists.Item>
));
}

Expand All @@ -117,26 +116,26 @@ const ExampleStory = () => {
}

return (
<CollapsibleList onChange={handleOnChange}>
<CollapsibleList.Heading>California Sports Teams</CollapsibleList.Heading>
<CollapsibleList.Group title={sportsData[0].sports[0].title}>
<CollapsibleChecklists onChange={handleOnChange}>
<CollapsibleChecklists.Heading>California Sports Teams</CollapsibleChecklists.Heading>
<CollapsibleChecklists.Group title={sportsData[0].sports[0].title}>
{renderTeams(sportsData[0].sports[0].teams)}
</CollapsibleList.Group>
<CollapsibleList.Group title={sportsData[0].sports[1].title}>
</CollapsibleChecklists.Group>
<CollapsibleChecklists.Group title={sportsData[0].sports[1].title}>
{renderTeams(sportsData[0].sports[1].teams)}
</CollapsibleList.Group>
<CollapsibleList.Group title={sportsData[0].sports[2].title} isDisabled>
</CollapsibleChecklists.Group>
<CollapsibleChecklists.Group title={sportsData[0].sports[2].title} isDisabled>
{renderTeams(sportsData[0].sports[2].teams)}
</CollapsibleList.Group>
</CollapsibleChecklists.Group>

<CollapsibleList.Heading>Ohio Sports Teams</CollapsibleList.Heading>
<CollapsibleList.Group title={sportsData[1].sports[0].title} onExpand={handleExpand}>
<CollapsibleChecklists.Heading>Ohio Sports Teams</CollapsibleChecklists.Heading>
<CollapsibleChecklists.Group title={sportsData[1].sports[0].title} onExpand={handleExpand}>
{renderOhioFootballTeams()}
</CollapsibleList.Group>
</CollapsibleChecklists.Group>

<CollapsibleList.Heading>Saskatchewan Sports Teams</CollapsibleList.Heading>
<CollapsibleChecklists.Heading>Saskatchewan Sports Teams</CollapsibleChecklists.Heading>
<div>None available (this shows that any child is accepted)</div>
</CollapsibleList>
</CollapsibleChecklists>
);
};

Expand Down
35 changes: 0 additions & 35 deletions packages/CollapsibleList/src/CollapsibleList.js

This file was deleted.

3 changes: 0 additions & 3 deletions packages/CollapsibleList/src/index.js

This file was deleted.

0 comments on commit b75fd0a

Please sign in to comment.