Skip to content

Commit

Permalink
feat(FieldsCombiner and TagGroup): QUV-1008 Use of styles prop, subco…
Browse files Browse the repository at this point in the history
…mponents and dot notation instead of extending styled components or use 'as' prop
  • Loading branch information
soslayando committed May 31, 2023
1 parent d801f48 commit c67f62e
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 108 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/components/ButtonGroup/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ButtonGroupProps
}

export const ButtonGroup: React.FC<ButtonGroupProps> = ({
as,
children,
hidden = false,
itemsGap = 'md',
Expand All @@ -40,7 +41,6 @@ export const ButtonGroup: React.FC<ButtonGroupProps> = ({
hidden={hidden}
itemsGap={itemsGap}
role={role}
size={size}
title={tooltip}
visibilityTrigger={visibilityTrigger}
>
Expand Down
58 changes: 0 additions & 58 deletions packages/core/src/components/TagGroup/StyledTagGroup.ts

This file was deleted.

59 changes: 34 additions & 25 deletions packages/core/src/components/TagGroup/TagGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
import * as React from 'react';
import { css, useTheme } from 'styled-components';

// components
import { Label } from '../Label';

// declarations
import { LabelPosition } from './declarations';
import { TagProps } from '../Tag';
import {
Flex,
GlobalAriaProps,
GlobalAttrProps,
StyledOverloadCssProps,
StyledPolymorphicProps,
} from '../../';

// styled
import {
StyledTagGroup,
StyledTagGroupLabel,
StyledTagGroupLabelProps,
StyledTagGroupList,
StyledTagGroupProps,
} from './StyledTagGroup';
// helpers
import { tagGroupLabelMixin } from './helpers';

export interface TagGroupProps
extends StyledPolymorphicProps,
StyledOverloadCssProps,
GlobalAttrProps,
GlobalAriaProps,
StyledTagGroupProps,
Omit<StyledTagGroupLabelProps, 'labelPosition'> {
/** Polymorphic prop to create a different tag or styled component
* https://styled-components.com/docs/api#as-polymorphic-prop */
Pick<TagProps, 'size'> {
children: React.ReactElement<TagProps>[];
/** Position of the label text relative to the tags */
labelPosition?: LabelPosition;
/** Text within the label. (aria-label is the same as Label) */
labelText?: string;
}
Expand All @@ -37,35 +35,46 @@ export const TagGroup: React.FC<TagGroupProps> = ({
children,
labelPosition = 'left',
labelText,
role = 'group',
size = 'md',
tooltip,
styles,
...restNativeProps
}) => {
const theme = useTheme();
const itemTokens = theme.cmp.tagGroup.item;
return (
<StyledTagGroup
<Flex
{...restNativeProps}
labelPosition={labelPosition}
role={role}
title={tooltip}
alignItems={labelPosition === 'left' ? 'center' : null}
inline
flexDirection={labelPosition === 'left' ? 'row' : 'column'}
styles={styles}
>
{labelText && (
<StyledTagGroupLabel
as={Label}
<Label
styles={tagGroupLabelMixin({ size, labelPosition, theme })}
size={size}
labelPosition={labelPosition}
>
{labelText}
</StyledTagGroupLabel>
</Label>
)}
<StyledTagGroupList size={size}>
<Flex
alignItems="center"
flexWrap="wrap"
inline
role="group"
// TODO: improve the genesys-tokens types to allow using the same types for space props (and many others), so we can pass a design token as a prop value
styles={css`
row-gap: ${itemTokens.space.margin.ver[size]};
column-gap: ${itemTokens.space.margin.hor[size]};
`}
>
{children?.map((child, idx) =>
React.cloneElement(child, {
key: idx,
size: size,
})
)}
</StyledTagGroupList>
</StyledTagGroup>
</Flex>
</Flex>
);
};
36 changes: 36 additions & 0 deletions packages/core/src/components/TagGroup/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { css, DefaultTheme } from 'styled-components';

import { LabelPosition } from './declarations';
import { StyledTagContainerProps } from '../Tag/components/StyledTagContainer';

export interface TagGroupLabelMixinProps
extends Pick<StyledTagContainerProps, 'size'> {
labelPosition: LabelPosition;
theme: DefaultTheme;
}

/**
* Get the specific styles for Label component when it's used as a TagGroup label component
*
* @return specific styles for Label
*/

export const tagGroupLabelMixin = ({
labelPosition,
size,
theme,
}: TagGroupLabelMixinProps) => {
const tokens = theme.cmp.tagGroup.label;
return css`
display: flex;
align-items: center;
flex-shrink: 0;
${labelPosition === 'left'
? css`
margin-right: ${tokens.space.marginRight};
`
: css`
margin-bottom: ${tokens.space.marginBottom[size]};
`};
`;
};
11 changes: 4 additions & 7 deletions packages/core/src/declarations/styled.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import React, { AllHTMLAttributes } from 'react';
import { CSSProp, DefaultTheme } from 'styled-components';
import { HTMLTag } from './commonProps';

/** ---------------------------------------------
* Polimorphic props for styled components
* --------------------------------------------- */
export interface StyledPolymorphicProps<T = any> {
/** Polymorphic prop to create a different tag or styled component
/** Polymorphic prop to create a different HTML tag based in the styled components one:
* https://styled-components.com/docs/api#as-polymorphic-prop */
as?: string | React.ComponentType<T>;
/** If you choose to wrap another component with the styled() HOC that also accepts an "as" prop,
* use "forwardedAs" to pass along the desired prop to the wrapped component.
* https://styled-components.com/docs/api#forwardedas-prop */
forwardedAs?: string | React.ComponentType<T>;
/** Only it's necessary when we need to use the polymorphic props 'as' and 'forwardedAs'. A string containing one or more class names for the element, used for CSS styling. */
as?: HTMLTag | React.ComponentType<T>;
/** Only it's necessary when we need to use the polymorphic props 'as'. A string containing one or more class names for the element, used for CSS styling. */
className?: AllHTMLAttributes<T>['className'];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export interface StoryWrapperProps extends StyledStoryWrapperProps {
children?: React.ReactNode;
className?: string;
display?: React.CSSProperties['display'];
/** If you choose to wrap another component with the styled() HOC that also accepts an "as" prop,
* use "forwardedAs" to pass along the desired prop to the wrapped component. */
/* https://styled-components.com/docs/api#forwardedas-prop */
forwardedAs?: string | React.ComponentType<any>;
/** Css height */
height?: React.CSSProperties['height'];
/** Css width */
Expand All @@ -28,7 +24,6 @@ export const StoryWrapper: React.FC<StoryWrapperProps> = ({
children,
className,
display = 'block',
forwardedAs,
height = '100%',
margin,
maxHeight,
Expand All @@ -55,7 +50,6 @@ export const StoryWrapper: React.FC<StoryWrapperProps> = ({
bgColor={bgColor}
className={className}
$display={display}
forwardedAs={forwardedAs}
$height={height}
margin={margin}
maxHeight={maxHeight}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Meta, StoryObj } from '@storybook/react';

import { FieldsCombiner } from '..';
import {
ElemIconButton,
ElemButton,
ElemSelect,
ElemCheckbox,
ElemInputControl,
} from './__stories__/commonElements';

const meta: Meta<typeof FieldsCombiner> = {
title: 'Components/Form/FieldsCombiner/cases',
component: FieldsCombiner,
args: {
hasWideControl: true,
size: 'md',
status: 'base',
},
};

export default meta;
type Story = StoryObj<typeof FieldsCombiner>;

export const InputAndButton: Story = {
name: 'Input and Button',
args: {
id: 'fields-1',
label: 'Input and Button to right',
leftElem: ElemInputControl,
rightElem: ElemButton,
},
};

export const InputAndIconButton: Story = {
name: 'Input and IconButton',
args: {
id: 'fields-1',
label: 'Input and IconButton to right',
leftElem: ElemInputControl,
rightElem: ElemIconButton,
},
};

export const InputAndCheckbox: Story = {
name: 'Input and Checkbox',
args: {
id: 'fields-1',
label: 'Input and Checkbox to right',
leftElem: ElemInputControl,
rightElem: ElemCheckbox,
},
};

export const SelectAndInput: Story = {
name: 'Select and Input',
args: {
id: 'fields-1',
label: 'Select and Input to right',
leftElem: ElemSelect,
rightElem: ElemInputControl,
},
};
15 changes: 15 additions & 0 deletions packages/form/src/components/FieldsCombiner/FieldsCombiner.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Meta, Canvas, Source, ArgTypes } from '@storybook/blocks';

import * as Stories from './FieldsCombiner.stories';
import * as CasesStories from './FieldsCombiner.cases.stories';

<Meta of={Stories} />

Expand All @@ -21,6 +22,20 @@ import { FieldsCombiner } from '@devoinc/genesys-ui-form';

<Canvas of={Stories.Base} />

## Cases

### Input and Button

<Canvas of={CasesStories.InputAndButton} />

### Input and IconButton

<Canvas of={CasesStories.InputAndIconButton} />

### Select and Input

<Canvas of={CasesStories.SelectAndInput} />

## Props

<ArgTypes of={Stories} />
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import { Meta, StoryObj } from '@storybook/react';

import { FieldsCombiner } from '..';
import {
ElemIconButton,
ElemButton,
ElemSelect,
ElemCheckbox,
ElemInputControl,
} from './__stories__/commonElements';
import { ElemButton, ElemSelect } from './__stories__/commonElements';

const meta: Meta<typeof FieldsCombiner> = {
title: 'Components/Form/FieldsCombiner',
Expand All @@ -25,8 +19,8 @@ type Story = StoryObj<typeof FieldsCombiner>;
export const Base: Story = {
args: {
id: 'fields-1',
label: 'Button to right with status',
label: 'Select and Button to right with status',
leftElem: ElemSelect,
rightElem: ElemCheckbox,
rightElem: ElemButton,
},
};
Loading

0 comments on commit c67f62e

Please sign in to comment.