Skip to content

Commit

Permalink
fix(composer):Scene Hierarchy radio buttons & bug fix for selection o…
Browse files Browse the repository at this point in the history
…n single click (#326)

* fix(composer): Implementing Scene Hierarchy radio buttons for single-click select & bug fix for selection on single click

* Implementing PR feedback

Co-authored-by: Emily Dodds <dodemily@amazon.com>
  • Loading branch information
mumanity and mumanity committed Nov 7, 2022
1 parent 037e1a4 commit 1026cb4
Show file tree
Hide file tree
Showing 23 changed files with 811 additions and 314 deletions.
4 changes: 2 additions & 2 deletions packages/scene-composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@
"global": {
"lines": 77.58,
"statements": 76.75,
"functions": 77.58,
"branches": 63.72,
"functions": 77.2,
"branches": 63.5,
"branchesTrue": 100
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import React from 'react';
import * as THREE from 'three';
import { useLoader } from '@react-three/fiber';

import { AnchorWidget } from '../../../../../src/augmentations/components/three-fiber/anchor/AnchorWidget';
import { DefaultAnchorStatus, KnownComponentType } from '../../../../../src';
import { useStore } from '../../../../../src/store';
import { AnchorWidget } from '../AnchorWidget';
import { DefaultAnchorStatus } from '../../../../..';
import { useStore } from '../../../../../store';

jest.mock('../../../../../src/augmentations/components/three-fiber/common/SvgIconToWidgetSprite', () =>
jest.mock('../../../three-fiber/common/SvgIconToWidgetSprite', () =>
jest.fn(((data, name, alwaysVisible, props) => <div data-test-id={name} {...props} />) as any),
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from 'react';
import { render } from '@testing-library/react';

import RadioButton from '../index';

describe('RadioButton', () => {
[
[
'First Option',
{
testId: 'one',
selected: false,
toggle: () => {
return null;
},
label: 'radio one',
},
],
[
'Second Option',
{
testId: 'two',
selected: true,
toggle: () => {
return null;
},
label: 'radio two',
},
],
[
'Third Option',
{
testId: 'three',
selected: false,
toggle: () => {
return null;
},
label: 'radio three',
},
],
[
'Fourth Option',
{
testId: 'four',
selected: false,
toggle: () => {
return null;
},
label: 'radio four',
},
],
[
'Fifth Option',
{
testId: 'five',
selected: false,
toggle: () => {
return null;
},
label: 'radio five',
},
],
].forEach((item) => {
it(`it should render the ${item[0]} as a radio button`, () => {
const data = item[1] as any;
const { selected, testId, toggle, label } = data;

const { getByTestId } = render(<RadioButton selected={selected} testId={testId} toggle={toggle} label={label} />);
const radioBtn: any = getByTestId(testId);

expect(radioBtn).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`RadioButton it should render the Fifth Option as a radio button 1`] = `
<input
data-testid="five"
type="radio"
value="radio five"
/>
`;

exports[`RadioButton it should render the First Option as a radio button 1`] = `
<input
data-testid="one"
type="radio"
value="radio one"
/>
`;

exports[`RadioButton it should render the Fourth Option as a radio button 1`] = `
<input
data-testid="four"
type="radio"
value="radio four"
/>
`;

exports[`RadioButton it should render the Second Option as a radio button 1`] = `
<input
checked=""
data-testid="two"
type="radio"
value="radio two"
/>
`;

exports[`RadioButton it should render the Third Option as a radio button 1`] = `
<input
data-testid="three"
type="radio"
value="radio three"
/>
`;
19 changes: 19 additions & 0 deletions packages/scene-composer/src/components/RadioButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { FC } from 'react';

import './radio-button.scss';

export interface RadioButtonProps {
checked?: boolean;
label?: string;
selected?: boolean;
testId?: string;
toggle: (e: any) => void;
}

const RadioButton: FC<RadioButtonProps> = ({ label, selected, testId, toggle }) => {
return <input checked={selected} data-testid={testId} onChange={toggle} type='radio' value={label} />;
};

RadioButton.displayName = 'RadioButton';

export default RadioButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
input[type="radio"] {
margin-right: 1rem;

// styled to match Polaris: https://polaris.a2z.com/components/radio-group/
&:checked {
appearance: none;
background-color: #fff;
color: #fff;
border: 4px solid #00a1c9;
border-radius: 50%;
padding: 3px;
}
}
41 changes: 32 additions & 9 deletions packages/scene-composer/src/components/Tree/TreeItem.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Button, Checkbox } from '@awsui/components-react';
import { Button } from '@awsui/components-react';
import React, { ComponentPropsWithRef, FC, ReactNode, useCallback } from 'react';

import RadioButton from '../RadioButton';
import { useStore } from '../../store';
import { useSceneComposerId } from '../../common/sceneComposerIdContext';

export type SelectionMode = 'single' | 'multi';

interface TreeItemInnerProps {
label?: string;
selected?: boolean;
selectable?: boolean;
className?: string;
Expand All @@ -23,33 +28,45 @@ export interface TreeItemProps extends TreeItemInnerProps, ComponentPropsWithRef

const TreeItemInner: FC<TreeItemInnerProps> = ({
children,
label,
onSelected = /* istanbul ignore next */ () => {},
selectable = true,
selected = false,
className = '',
onActivated = () => {},
}) => {
const sceneComposerId = useSceneComposerId();
const setSelectedSceneNodeRef = useStore(sceneComposerId).getState().setSelectedSceneNodeRef;

const selectFromChildren = () => {
(children as any)?.forEach((child) => {
return child ? setSelectedSceneNodeRef(child.props?.objectRef) : null;
});
};

const toggle = useCallback(
(e) => {
onSelected(!selected, e);
return (children) =>
children.every((child) => child === true) ? selectFromChildren() : onSelected(!selected, e);
},
[selected, onSelected],
);

return (
<div
className={`tm-tree-item-inner${selected ? ' selected' : ''} ${className}`.trimEnd()}
<label
className={`tm-tree-item-inner ${selected ? 'selected' : ''} ${className}`.trimEnd()}
onClick={toggle}
onDoubleClick={onActivated}
aria-selected={selected}
aria-selected={selected as boolean}
>
{selectable && (
<Checkbox checked={selected} onChange={toggle}>
<>
<RadioButton selected={selected} toggle={onSelected} label={label} />
{children}
</Checkbox>
</>
)}
{!selectable && children}
</div>
</label>
);
};

Expand Down Expand Up @@ -88,7 +105,13 @@ const TreeItem = React.forwardRef<HTMLLIElement, TreeItemProps>(
role='treeitem'
{...props}
>
<TreeItemInner selected={selected} selectable={selectable} onActivated={onActivated} onSelected={onSelected}>
<TreeItemInner
selected={selected}
selectable={selectable}
onActivated={onActivated}
onSelected={onSelected}
label={(labelText as any)?.props?.labelText || ''}
>
{expandable && (
<Button
className='tm-tree-item-expand-btn'
Expand Down
Loading

0 comments on commit 1026cb4

Please sign in to comment.