Skip to content

Commit

Permalink
fix: radiobutton: multiple radio buttons on the same page doesnt work…
Browse files Browse the repository at this point in the history
… as intended (#253)

* fix: radiobutton: multiple radio buttons on the same page doesnt work as intended

also adds header action buttons to panel and normalizes close button props

* chore: radiobutton: update snapshots
  • Loading branch information
dkilgore-eightfold authored Jul 25, 2022
1 parent 04ef8f0 commit 664c531
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/components/Dialog/BaseDialog/BaseDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export const BaseDialog: FC<BaseDialogProps> = React.forwardRef(
actionButtonOneProps,
actionButtonTwoProps,
actionButtonThreeProps,
closeButtonProps,
closeIcon = IconName.mdiClose,
parent = document.body,
visible,
onClose,
Expand Down Expand Up @@ -106,8 +108,10 @@ export const BaseDialog: FC<BaseDialogProps> = React.forwardRef(
<NeutralButton {...actionButtonOneProps} />
)}
<NeutralButton
iconProps={{ path: IconName.mdiClose }}
ariaLabel={'Close'}
iconProps={{ path: closeIcon }}
onClick={onClose}
{...closeButtonProps}
/>
</span>
</div>
Expand Down
11 changes: 11 additions & 0 deletions src/components/Dialog/BaseDialog/BaseDialog.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, { Ref } from 'react';
import { OcBaseProps } from '../../OcBase';
import { ButtonProps } from '../../Button';
import { IconName } from '../../Icon';

export type CloseButtonProps = Omit<ButtonProps, 'onClick' | 'icon'>;

type EventType =
| React.KeyboardEvent<HTMLDivElement>
Expand All @@ -22,6 +25,14 @@ export interface BaseDialogProps
* Props for the third header action button
*/
actionButtonThreeProps?: ButtonProps;
/**
* Close button extra props
*/
closeButtonProps?: CloseButtonProps;
/**
* Close icon name
*/
closeIcon?: IconName;
/**
* Dialog is visible or not
*/
Expand Down
4 changes: 4 additions & 0 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const Dialog: FC<DialogProps> = React.forwardRef(
actionButtonOneProps,
actionButtonTwoProps,
actionButtonThreeProps,
closeButtonProps,
closeIcon,
parent = document.body,
size = DialogSize.medium,
headerClassNames,
Expand Down Expand Up @@ -52,6 +54,8 @@ export const Dialog: FC<DialogProps> = React.forwardRef(
actionButtonOneProps={actionButtonOneProps}
actionButtonTwoProps={actionButtonTwoProps}
actionButtonThreeProps={actionButtonThreeProps}
closeButtonProps={closeButtonProps}
closeIcon={closeIcon}
dialogClassNames={dialogClasses}
headerClassNames={headerClasses}
bodyClassNames={bodyClasses}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const Modal: FC<ModalProps> = React.forwardRef(
actionButtonOneProps,
actionButtonTwoProps,
actionButtonThreeProps,
closeButtonProps,
closeIcon,
size = ModalSize.medium,
headerClassNames,
bodyClassNames,
Expand Down Expand Up @@ -51,6 +53,8 @@ export const Modal: FC<ModalProps> = React.forwardRef(
actionButtonOneProps={actionButtonOneProps}
actionButtonTwoProps={actionButtonTwoProps}
actionButtonThreeProps={actionButtonThreeProps}
closeButtonProps={closeButtonProps}
closeIcon={closeIcon}
dialogWrapperClassNames={modalWrapperClassNames}
dialogClassNames={modalClasses}
headerClassNames={headerClasses}
Expand Down
43 changes: 43 additions & 0 deletions src/components/Panel/Panel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,33 @@ const Top_Story: ComponentStory<typeof Panel> = (args) => {

export const Top = Top_Story.bind({});

const Header_Actions_Story: ComponentStory<typeof Panel> = (args) => {
const [visible, setVisible] = useState<boolean>(false);
return (
<>
<PrimaryButton
text={'Open panel'}
onClick={() => setVisible(true)}
/>
<Panel
{...args}
footer={
<div>
<PrimaryButton
text={'Close'}
onClick={() => setVisible(false)}
/>
</div>
}
visible={visible}
onClose={() => setVisible(false)}
/>
</>
);
};

export const Header_Actions = Header_Actions_Story.bind({});

const panelArgs: Object = {
size: PanelSize.small,
visible: false,
Expand Down Expand Up @@ -467,3 +494,19 @@ Top.args = {
size: PanelSize.small,
placement: 'top',
};

Header_Actions.args = {
...panelArgs,
actionButtonOneProps: {
iconProps: { path: IconName.mdiCogOutline },
},
actionButtonTwoProps: {
iconProps: {
path: IconName.mdiHistory,
},
},
actionButtonThreeProps: {
iconProps: { path: IconName.mdiDatabaseArrowDownOutline },
},
size: PanelSize.medium,
};
24 changes: 22 additions & 2 deletions src/components/Panel/Panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import Enzyme, { mount, ReactWrapper } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import MatchMediaMock from 'jest-matchmedia-mock';
import { Panel } from './';
import { create } from 'react-test-renderer';
import { Tab, Tabs } from '../Tabs';
import { IconName } from '../Icon';

Enzyme.configure({ adapter: new Adapter() });

Expand Down Expand Up @@ -65,4 +64,25 @@ describe('Panel', () => {
wrapper.find('.panel-backdrop').at(0).simulate('click');
expect(onClose).toHaveBeenCalledTimes(2);
});

test('panel header actions exist', () => {
wrapper.setProps({
visible: true,
actionButtonOneProps: {
classNames: 'header-action-button-1',
iconProps: { path: IconName.mdiCogOutline },
},
actionButtonTwoProps: {
classNames: 'header-action-button-2',
iconProps: { path: IconName.mdiHistory },
},
actionButtonThreeProps: {
classNames: 'header-action-button-3',
iconProps: { path: IconName.mdiDatabaseArrowDownOutline },
},
});
expect(wrapper.find('.header-action-button-1').length).toBeTruthy();
expect(wrapper.find('.header-action-button-2').length).toBeTruthy();
expect(wrapper.find('.header-action-button-3').length).toBeTruthy();
});
});
30 changes: 22 additions & 8 deletions src/components/Panel/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const PANEL_WIDTHS: Record<PanelSize, number> = Object.freeze({
export const Panel = React.forwardRef<PanelRef, PanelProps>(
(
{
actionButtonOneProps,
actionButtonTwoProps,
actionButtonThreeProps,
size = PanelSize.medium,
visible = false,
closable = true,
Expand Down Expand Up @@ -108,14 +111,25 @@ export const Panel = React.forwardRef<PanelRef, PanelProps>(
const getHeader = (): JSX.Element => (
<div className={headerClasses}>
<div>{title}</div>
{closable && (
<NeutralButton
iconProps={{ path: closeIcon }}
ariaLabel={'Close'}
onClick={onClose}
{...closeButtonProps}
/>
)}
<span className={styles.headerButtons}>
{actionButtonThreeProps && (
<NeutralButton {...actionButtonThreeProps} />
)}
{actionButtonTwoProps && (
<NeutralButton {...actionButtonTwoProps} />
)}
{actionButtonOneProps && (
<NeutralButton {...actionButtonOneProps} />
)}
{closable && (
<NeutralButton
iconProps={{ path: closeIcon }}
ariaLabel={'Close'}
onClick={onClose}
{...closeButtonProps}
/>
)}
</span>
</div>
);

Expand Down
12 changes: 12 additions & 0 deletions src/components/Panel/Panel.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ type EventType =
export type CloseButtonProps = Omit<ButtonProps, 'onClick' | 'icon'>;

export interface PanelProps extends Omit<OcBaseProps<HTMLElement>, 'title'> {
/**
* Props for the first header action button
*/
actionButtonOneProps?: ButtonProps;
/**
* Props for the second header action button
*/
actionButtonTwoProps?: ButtonProps;
/**
* Props for the third header action button
*/
actionButtonThreeProps?: ButtonProps;
/**
* Autofocus on the panel on visible
* @default true
Expand Down
8 changes: 8 additions & 0 deletions src/components/Panel/panel.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
top: 0;
display: flex;
justify-content: space-between;

&-buttons {
align-items: flex-end;
align-self: start;
height: fit-content;
justify-content: right;
white-space: nowrap;
}
}

.body {
Expand Down
72 changes: 68 additions & 4 deletions src/components/RadioButton/RadioButton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { Stories } from '@storybook/addon-docs';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RadioButton, RadioButtonValue, RadioGroup } from './';
import { Stack } from '../Stack';

export default {
title: 'Radio Button',
Expand Down Expand Up @@ -106,9 +107,23 @@ export default {
},
} as ComponentMeta<typeof RadioButton>;

const RadioButton_Story: ComponentStory<typeof RadioButton> = (args) => (
<RadioButton {...args} />
);
const RadioButton_Story: ComponentStory<typeof RadioButton> = (args) => {
const [selected, setSelected] = useState<RadioButtonValue>('label1');

const radioChangeHandler = (
e?: React.ChangeEvent<HTMLInputElement>
): void => {
setSelected(e.target.value);
};

return (
<RadioButton
{...args}
checked={selected === 'Label1'}
onChange={radioChangeHandler}
/>
);
};

export const Radio_Button = RadioButton_Story.bind({});

Expand All @@ -128,6 +143,50 @@ const RadioGroup_Story: ComponentStory<typeof RadioGroup> = (args) => {

export const Radio_Group = RadioGroup_Story.bind({});

const Bespoke_RadioGroup_Story: ComponentStory<typeof RadioButton> = (args) => {
const [selected, setSelected] = useState<RadioButtonValue>('label1');

const radioChangeHandler = (
e?: React.ChangeEvent<HTMLInputElement>
): void => {
setSelected(e.target.value);
};

return (
<Stack direction="vertical" gap="m">
<RadioButton
{...args}
ariaLabel={'Label 1'}
checked={selected === 'label1'}
id={'myRadioButtonId1'}
label={'Label 1'}
onChange={radioChangeHandler}
value={'label1'}
/>
<RadioButton
{...args}
ariaLabel={'Label 2'}
checked={selected === 'label2'}
id={'myRadioButtonId2'}
label={'Label 2'}
onChange={radioChangeHandler}
value={'label2'}
/>
<RadioButton
{...args}
ariaLabel={'Label 3'}
checked={selected === 'label3'}
id={'myRadioButtonId3'}
label={'Label 3'}
onChange={radioChangeHandler}
value={'label3'}
/>
</Stack>
);
};

export const Bespoke_Radio_Group = Bespoke_RadioGroup_Story.bind({});

const radioButtonArgs: Object = {
allowDisabledFocus: false,
ariaLabel: 'Label',
Expand All @@ -136,7 +195,7 @@ const radioButtonArgs: Object = {
classNames: 'my-radiobutton-class',
disabled: false,
name: 'myRadioButtonName',
value: 'Label',
value: 'Label1',
id: 'myRadioButtonId',
};

Expand All @@ -155,3 +214,8 @@ Radio_Group.args = {
})),
layout: 'vertical',
};

Bespoke_Radio_Group.args = {
...radioButtonArgs,
name: 'roleGroupName',
};
Loading

0 comments on commit 664c531

Please sign in to comment.