Skip to content
This repository has been archived by the owner on Jul 27, 2023. It is now read-only.

Feature 69 - ButtonsGroup [Dev] #81

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
5 changes: 5 additions & 0 deletions packages/components/src/ButtonsGroup/ButtonsGroup.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { HTMLProps } from 'react';

export interface ButtonsGroupProps extends HTMLProps<HTMLButtonElement> {}

export default function ButtonsGroup(props: ButtonsGroupProps): JSX.Element;
165 changes: 165 additions & 0 deletions packages/components/src/ButtonsGroup/ButtonsGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { forwardRef, useEffect, useState } from 'react';
import { createStyleSheet } from '@platzily-ui/styling';
import { PropTypes } from 'prop-types';

const useStyleSheet = createStyleSheet(
(theme, { borderWrapperComponent }) => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

borderWrapperComponent? 🤔 this name makes me thinks that it is a component, but is a border radius unit right? maybe a name that specifies that? Like wrapperRadius?

buttonsGroupWrapper: {
width: 'auto',
height: 'auto',
display: 'inline-block',
borderStyle: 'solid',
borderColor: theme.palette.neutral.secondary,
borderWidth: 1,
borderRadius: borderWrapperComponent || theme.spacing(),
},
firstButtonStyle:{
borderTopLeftRadius: borderWrapperComponent || theme.spacing(),
borderBottomLeftRadius: borderWrapperComponent || theme.spacing(),
},
lastButtonStyle:{
borderTopRightRadius: borderWrapperComponent || theme.spacing(),
borderBottomRightRadius: borderWrapperComponent || theme.spacing(),
},
separationLinesButton: {
borderRightStyle: 'solid',
borderRightColor: theme.palette.neutral.secondary,
borderRightWidth: 1,
},
buttonsStyles:{
padding: '5px 10px',
},
buttonUnselected: {
backgroundColor: theme.palette.neutral.light,
color: theme.palette.neutral.dark
},
buttonSelected: {
backgroundColor: theme.palette.primary.main,
color: theme.palette.neutral.light
},
}),
{ key: 'ButtonsGroup' },
);

const ButtonsGroup = forwardRef(function ButtonsGroup(props, ref) {
const { actions, className, classNameButtons, separationLinesButtonProp, selectedStyles, unselectedStyles, selectedButtonDefault, borderWrapperComponent, ...otherProps } = props;
const [state, setState] = useState(actions);
const { classes, cx } = useStyleSheet(props);
const stateLength = actions.length;

useEffect(() => {
if (selectedButtonDefault <= (state.length - 1)) {
setState(state.map((element, indexMap) => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does the same as setStateRender right?

(selectedButtonDefault === indexMap)
? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ternary can be avoided if you return the object this way:

return {
...element,
selected: index === indexMap
}

...element,
selected: true,
}
: {
...element,
selected: false,
}
)));
} else {
setState(state.map((element, indexMap) => (
(indexMap === 0)
? {
...element,
selected: true,
}
: {
...element,
selected: false,
}
)));
}
},[]);



const selectButtonStyles = (stateButton) => {
return stateButton ? (selectedStyles || classes.buttonSelected) : (unselectedStyles || classes.buttonUnselected);
};

const cornerButtonsGroup = (index) => {
let styleButton;
if (index === 0) { styleButton = classes.firstButtonStyle; }
if (index === (stateLength - 1)) { styleButton = classes.lastButtonStyle; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of doing this haha.

I would prefer:

if (index === (stateLength - 1)) {
    styleButton = classes.lastButtonStyle; 
}


return styleButton;
};

const separationLinesButton = (index) => {
let separationStyle;
if (index !== (stateLength - 1)) { separationStyle = separationLinesButtonProp || classes.separationLinesButton; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition can be in more than one line.


return separationStyle;
};

function setStateRender(index) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is a good thing abstrating the selected style with this function, but that meas that in the actions the user should not send the selected key anymore right? Why dont we remove it?

setState(state.map((element, indexMap) => (
(index === indexMap)
? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ternary can be avoided if you return the object this way:

return {
   ...element,
   selected: index === indexMap
}

...element,
selected: true,
}
: {
...element,
selected: false,
}
)));
};

return (
<div ref={ref} className={cx(classes.buttonsGroupWrapper, className)} {...otherProps}>
{state.map((button, index) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

State needs to be an array always, so if the prop of actions is an object it will break.

const { selected, childrenButton, onClick } = button;
return (
<button
type="button"
key={index}
onClick={() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we abstract this to another function?

setStateRender(index);
onClick();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We needs to pass the event to the onClick prop, lets send also the index of the button so the user can identify it.

}}
className={cx(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use the object approach for these validations 🤔

selectButtonStyles(selected),
cornerButtonsGroup(index),
separationLinesButton(index),
classes.buttonsStyles,
classNameButtons
)}
>
{childrenButton}
</button>
);
})}
</div>
);
});

ButtonsGroup.propTypes = {
actions: PropTypes.oneOfType([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this prop type be different?

acions: PropTypes.arrayOf(PropTypes.shape({
      childrenButton: PropTypes.element,
      onClick: PropTypes.func,
      selected: PropTypes.boolean,
})).isRequired

PropTypes.arrayOf(PropTypes.object).isRequired,
PropTypes.shape({
childrenButton: PropTypes.element,
onClick: PropTypes.func,
selected: PropTypes.boolean,
}).isRequired,
]),
borderWrapperComponent: PropTypes.number,
className: PropTypes.string,
classNameButtons: PropTypes.string,
selectedButtonDefault: PropTypes.number,
selectedStyles: PropTypes.string,
separationLinesButtonProp: PropTypes.string,
unselectedStyles: PropTypes.string,
};

ButtonsGroup.defaultProps = {
className: '',
separationLinesButtonProp: undefined,

};

export default ButtonsGroup;
44 changes: 44 additions & 0 deletions packages/components/src/ButtonsGroup/ButtonsGroup.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { render } from '@testing-library/react';
import { ThemeProvider, createTheme } from '@platzily-ui/styling';
import ButtonsGroup from './ButtonsGroup';

describe('@Components/ButtonsGroup', () => {
it('Given the ButtonsGroup Component, when the props provide borderWrapperComponent with a number then the ButtonsGroup component will take border-radius attribute', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add more tests here, we could test that for example the buttons rendered are the same length as the actions. And the last clicked button has the selected styles.

// arrange
const actions = [
{
childrenButton: 'Button One',
selected: false,
onClick: () => {},
},
{
childrenButton: 'Button Two',
selected: false,
onClick: () => {},
},
{
childrenButton: 'Button Three',
selected: false,
onClick: () => {},
},
];

const { getByRole } = render(
<ThemeProvider theme={createTheme()}>
<ButtonsGroup
role="banner"
actions={actions}
borderWrapperComponent={5}
/>
</ThemeProvider>,
);

// act
const ButtonsGroupTestedText = getByRole('banner');

// assert
expect(ButtonsGroupTestedText).toBeDefined();
expect(ButtonsGroupTestedText).toHaveStyle(`border-radius:5px`);
});

});
2 changes: 2 additions & 0 deletions packages/components/src/ButtonsGroup/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ButtonsGroup';
export { default } from './ButtonsGroup';
1 change: 1 addition & 0 deletions packages/components/src/ButtonsGroup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ButtonsGroup';
3 changes: 3 additions & 0 deletions packages/components/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ export { default as Button } from './Button';

export * from './SvgIcon';
export { default as SvgIcon } from './SvgIcon';

export * from './ButtonsGroup';
export { default as ButtonsGroup } from './ButtonsGroup';
1 change: 1 addition & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { default as Text } from './Text';
export { default as Header } from './Header';
export { default as Button } from './Button';
export { default as SvgIcon } from './SvgIcon';
export { default as ButtonsGroup } from './ButtonsGroup';
50 changes: 50 additions & 0 deletions packages/docs/components/ButtonsGroup/ButtonsGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ButtonsGroup } from '@platzily-ui/components';
import { Fragment, useState } from 'react';
import { createStyleSheet } from '@platzily-ui/styling';

const useStyleSheet = createStyleSheet(
(theme) => ({
buttonsGroupWrapper: {
borderColor: theme.palette.neutral.secondary,
},
}),
{ key: 'ButtonsGroupImplementation' },
);

export default function ButtonsGroupComponent(props) {
const { classes } = useStyleSheet(props);

const [render, setRender] = useState('');

const actions = [
{
childrenButton: 'Button One',
selected: false,
onClick: () => setRender('One'),
},
{
childrenButton: 'Button Two',
selected: false,
onClick: () => setRender('Two'),
},
{
childrenButton: 'Button Three',
selected: false,
onClick: () => setRender('Three'),
},
];

return (
<Fragment >
<span style={{ display:'flex', justifyContent:'center', margin:'20px' }}>
<ButtonsGroup
actions={actions}
className={ classes.buttonsGroupWrapper }
classNameButtons = { classes.ButtonsStyles }
/>
</span>
<p style={{ textAlign:'center', margin:'20px' }}> Actions {render}</p>
</Fragment>
);
}

11 changes: 10 additions & 1 deletion packages/docs/components/styling/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import UseThemeStyledBoxComponent from './UseThemeStyledBox';
import ColorComponent from './ColorComponent';

import HeaderComponent from '../Header/Header';
import ButtonsGroupComponent from '../ButtonsGroup/ButtonsGroup';

import { defaultTheme, customTheme } from './theme';

Expand Down Expand Up @@ -55,7 +56,15 @@ export function Color({ color, colorType }) {
export function Header() {
return (
<ThemeProvider theme={defaultTheme}>
<HeaderComponent />
<HeaderComponent />
</ThemeProvider>
);
}

export function ButtonsGroup() {
return (
<ThemeProvider theme={defaultTheme}>
<ButtonsGroupComponent />
</ThemeProvider>
);
}
Loading