Skip to content

Commit

Permalink
Challenge Added: Nested Checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
SujithGunasekaran authored and sadanandpai committed Aug 18, 2024
1 parent 1ee6fd5 commit 3e24e11
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 0 deletions.
110 changes: 110 additions & 0 deletions apps/react/src/challenges/nested-checkbox/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useState } from 'react';
import CheckBoxList from './CheckBoxList';
import { checkboxList } from './constants';
import styles from './styles.module.css';

const NestedCheckbox = () => {
const [checkList, setCheckList] = useState(checkboxList);

const toggleAllChildNodes = (list, value) => {
for (let i = 0; i < list.length; i++) {
list[i].checked = value;
toggleAllChildNodes(list[i].children, value);
}
};

const dfs = (list, id, value, isFound) => {
if (list.length === 0) return isFound;
for (let i = 0; i < list.length; i++) {
if (list[i].id === id) {
list[i].checked = value;
isFound = true;
toggleAllChildNodes(list[i].children, value);
break;
}
isFound = dfs(list[i].children, id, value, isFound);
if (isFound) break;
}
return isFound;
};

const getActiveChildCount = (list) => {
if (list.length === 0) return 0;
let count = 0;
for (let i = 0; i < list.length; i++) {
// If it's checked then increase the count.
if (list[i].checked) {
count += 1;
}

// If there is no children for the node then we skip the rest of the code.
if (list[i].children.length === 0) continue;

// we need to get the active child count.
let childCount = getActiveChildCount(list[i].children);

// If count is not equal then we need to un-check the parent node else we need to check the parent node.
if (childCount !== list[i].children.length) {
count = list[i].checked ? count - 1 : count;
list[i].checked = false;
} else {
count = list[i].checked ? count : count + 1;
list[i].checked = true;
}
}
return count;
};

const handleChange = (id, value) => {
let clone = JSON.parse(JSON.stringify(checkList));
let isFound = false;
let parentIndex = 0;
/**
* Iterate the root node and the child node until we find the target node.
* If we found the target node we need to make all the children of the target flag as the selected value ( true | false ).
*/
for (let i = 0; i < clone.length; i++) {
if (clone[i].id === id) {
clone[i].checked = value;
isFound = true;
parentIndex = i;
toggleAllChildNodes(clone[i].children, value);
break;
}

// If the target node is not a root. We need to check all the child nodes.
isFound = dfs(clone[i].children, id, value, false);

// If we found the targetNode we no need to check the rest of the node.
if (isFound) {
parentIndex = i;
break;
}
}

/**
* Now we want to check/unCheck the parent node based on the child node values.
* If all the child nodes are check we need to make the parent node check.
* if any one of the child nodes are unChecked we need to make the parent node un-check.
*/
const childCount = getActiveChildCount(clone[parentIndex].children);
if (clone[parentIndex].children.length > 0) {
if (childCount !== clone[parentIndex].children.length) {
clone[parentIndex].checked = false;
} else {
clone[parentIndex].checked = true;
}
}

// Finally set the updated list.
setCheckList(clone);
};

return (
<div className={styles.nested_checkbox_wrapper}>
{<CheckBoxList childCheckBoxList={checkList} onCheckBoxChange={handleChange} />}
</div>
);
};

export default NestedCheckbox;
19 changes: 19 additions & 0 deletions apps/react/src/challenges/nested-checkbox/CheckBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import styles from './styles.module.css';

const Checkbox = (props) => {
const { id, isChecked, label, onCheckBoxChange } = props;

return (
<label className={styles.nested_checkbox_label}>
<input
type="checkbox"
checked={isChecked}
onChange={() => onCheckBoxChange(id, !isChecked)}
/>
{label}
</label>
);
};

export default Checkbox;
31 changes: 31 additions & 0 deletions apps/react/src/challenges/nested-checkbox/CheckBoxList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable react/prop-types */
import { Fragment } from 'react';
import Checkbox from './CheckBox';
import styles from './styles.module.css';

const CheckBoxList = (props) => {
const { childCheckBoxList, onCheckBoxChange } = props;

return (
<Fragment>
{childCheckBoxList.map((childBox) => (
<div key={childBox.id} className={styles.nested_checkbox_child}>
<Checkbox
id={childBox.id}
label={childBox.label}
isChecked={childBox.checked}
onCheckBoxChange={onCheckBoxChange}
/>
{childBox.children.length > 0 && (
<CheckBoxList
childCheckBoxList={childBox.children}
onCheckBoxChange={onCheckBoxChange}
/>
)}
</div>
))}
</Fragment>
);
};

export default CheckBoxList;
110 changes: 110 additions & 0 deletions apps/react/src/challenges/nested-checkbox/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
export const checkboxList = [
{
label: 'p1',
id: 1,
checked: false,
children: [
{
label: 'p1-c1',
id: 2,
checked: false,
children: [
{
label: 'p1-c1-c1',
id: 3,
checked: false,
children: [],
},
{
label: 'p1-c1-c2',
id: 4,
checked: false,
children: [
{
label: 'p1-c1-c2-c1',
id: 5,
checked: false,
children: [],
},
{
label: 'p1-c1-c2-c2',
id: 6,
checked: false,
children: [
{
label: 'p1-c1-c2-c2-c1',
id: 7,
checked: false,
children: [],
},
{
label: 'p1-c1-c2-c2-c2',
id: 8,
checked: false,
children: [],
},
],
},
{
label: 'p1-c1-c2-c3',
id: 9,
checked: false,
children: [],
},
],
},
],
},
{
label: 'p1-c2',
id: 10,
checked: false,
children: [],
},
{
label: 'p1-c3',
id: 11,
checked: false,
children: [],
},
],
},
{
label: 'p2',
id: 12,
checked: false,
children: [
{
label: 'p2-c1',
id: 13,
checked: false,
children: [],
},
{
label: 'p2-c2',
id: 14,
checked: false,
children: [],
},
],
},
{
label: 'p3',
id: 15,
checked: false,
children: [
{
label: 'p3-c1',
id: 16,
checked: false,
children: [],
},
],
},
{
label: 'p4',
id: 17,
checked: false,
children: [],
},
];
15 changes: 15 additions & 0 deletions apps/react/src/challenges/nested-checkbox/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.nested_checkbox_wrapper {
margin: 20px 0px;
width: 300px;
}

.nested_checkbox_label {
display: flex;
align-items: center;
gap: 4px;
}

.nested_checkbox_child {
margin: 10px 0px;
padding: 0px 0px 0px 25px;
}
2 changes: 2 additions & 0 deletions apps/react/src/pages/Challenge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import DraggableList from '@/challenges/drag-drop/DraggableList';
import Circles from '@/challenges/circles/circles';
import AnalogClock from '@/challenges/analog-clock/analog-clock';
import AdvancedCounter from '@/challenges/advanced-counter/advanced-counter';
import NestedCheckbox from '@/challenges/nested-checkbox/App';

const reactChallengesMap = {
'transfer-list': <TransferListApp />,
Expand Down Expand Up @@ -129,6 +130,7 @@ const reactChallengesMap = {
'drag-drop': <DraggableList />,
circles: <Circles />,
'analog-clock': <AnalogClock />,
'nested-checkbox': <NestedCheckbox />,
};

function Challenge() {
Expand Down
7 changes: 7 additions & 0 deletions shared/data/content/contributors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,11 @@ export const contributors = new Map<string, IContributor>([
'officialbidisha',
{ name: 'Bidisha Das', pic: 'https://avatars.githubusercontent.com/u/49115207' },
],
[
'SujithGunasekaran',
{
name: 'Sujith Gunasekaran',
pic: 'https://avatars.githubusercontent.com/u/68234378?s=96&v=4',
},
],
]);
10 changes: 10 additions & 0 deletions shared/data/content/react-challenges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,16 @@ const challenges = new Map<string, IChallenge>([
tags: [ETag.interview],
},
],
[
'nested-checkbox',
{
title: 'Nested Checkbox',
link: 'nested-checkbox',
difficulty: EDifficulty.Hard,
developer: 'SujithGunasekaran',
tags: [ETag.interview],
},
],
]);

export const reactChallenges = sortChallengesByDifficulty(challenges);

0 comments on commit 3e24e11

Please sign in to comment.