Skip to content

Commit

Permalink
Merge pull request #854 from vrk-kpa/feature/margin-provider
Browse files Browse the repository at this point in the history
[Feature] Margin provider and CSS-based margin props
  • Loading branch information
LJKaski authored Jun 7, 2024
2 parents 7bb8369 + 1839b5c commit 77ccd84
Show file tree
Hide file tree
Showing 171 changed files with 3,023 additions and 980 deletions.
4 changes: 2 additions & 2 deletions .styleguidist/spacingprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { Button } from 'suomifi-ui-components';
</div>;
```

### Overriding with style attribute
### Margin props and style attributes

Margin properties add inline styling to the rendered HTML. If the component interface also accepts a style attribute, it takes precedence over margin property.
Margin properties add CSS styling marked `!important` so it acts as an override all other styles, including HTML style attributes.

```jsx
import { Button } from 'suomifi-ui-components';
Expand Down
132 changes: 132 additions & 0 deletions .styleguidist/spacingprovider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
Spacing provider is a wrapper component that allows giving its children margin rules to follow. You can specify the rules per component, and all components of the given type will follow the margin rules.

The margin values can be given using the desired spacing token.

### Basic example

```js
import {
SpacingProvider,
TextInput,
Button,
WizardNavigation,
WizardNavigationItem,
RouterLink
} from 'suomifi-ui-components';

const Comp = (props) => {
const { children, ...passProps } = props;
return <div {...passProps}>{props.children}</div>;
};

<SpacingProvider
margins={{
textInput: { margin: 's' },
button: { mb: 'm', ml: 's' }
}}
>
<div style={{ border: '1px dotted blue' }}>
<TextInput labelText="First name" />
<TextInput labelText="Last name" />
<Button>Submit</Button>
</div>
</SpacingProvider>;
```

### Overriding global margins

The global margins given via the provider are set as low specificity css styles, and can thus be easily overridden where needed. Override can be made using one of these options:

- CSS styles using class selectors
- Styled-component with margin specified
- An inner `SpacingProvider` wrapper
- Component-specific margin props

In the example below all the buttons are inside a spacing provider with margins rules for buttons, but some of them have their styles overridden using the above methods.

```js
import {
SpacingProvider,
TextInput,
Button,
Paragraph
} from 'suomifi-ui-components';
import { default as styled } from 'styled-components';

const StyledButton = styled(Button)`
margin: 0;
`;

<div
style={{
border: '1px dotted blue',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
width: '100%'
}}
>
<SpacingProvider
margins={{
button: { margin: 'm' }
}}
>
<Button>Button with global margins</Button>
<SpacingProvider margins={{ button: { margin: 0 } }}>
<Button>Margins overridden with nested provider</Button>
</SpacingProvider>
<Button>Button with global margins</Button>
<StyledButton>
Margins overridden using styled-components
</StyledButton>
<Button>Button with global margins</Button>
<Button margin="0">Margins overridden with margin prop</Button>
</SpacingProvider>
</div>;
```

### An example form

With the provider developers can generate a margin ruleset once for a specific use case e.g. a form. The ruleset along with the `SpacingProvider` component can then be used to quickly create layouts in similar cases.

```js
import {
Button,
TextInput,
Textarea,
Checkbox,
Paragraph,
Heading,
SpacingProvider
} from 'suomifi-ui-components';

<div
style={{
border: '1px dotted blue',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start'
}}
>
<SpacingProvider
margins={{
all: { mb: 'xs' },
button: { mb: 'xxs' }
}}
>
<Heading variant="h2">Feedback</Heading>
<Paragraph>Please tell us how we did.</Paragraph>
<TextInput
onBlur={(event) => console.log(event.target.value)}
labelText="Name"
/>
<TextInput
type="email"
onBlur={(event) => console.log(event.target.value)}
labelText="Email"
/>
<Textarea labelText="Your message" />
<Button>Submit</Button>
</SpacingProvider>
</div>;
```
4 changes: 4 additions & 0 deletions .styleguidist/styleguidist.sections.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ module.exports = {
name: 'Tokens',
content: './.styleguidist/spacingtokens.md',
},
{
name: 'Global spacing',
content: './.styleguidist/spacingprovider.md',
},
],
},
{
Expand Down
8 changes: 7 additions & 1 deletion src/core/ActionMenu/ActionMenu.baseStyles.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { css } from 'styled-components';
import { MarginProps, buildSpacingCSS } from '../theme/utils/spacing';

export const baseStyles = () => css`
export const baseStyles = (
globalMargins?: MarginProps,
propMargins?: MarginProps,
) => css`
${buildSpacingCSS(globalMargins)}
${buildSpacingCSS(propMargins, true)}
&.fi-action-menu--full-width {
width: 100%;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/ActionMenu/ActionMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('Margin prop', () => {
const { container } = render(
TestActionMenu({ ...actionMenuProps, margin: 'xs' }),
);
expect(container.firstChild).toHaveAttribute('style', 'margin: 10px;');
expect(container.firstChild).toHaveStyle('margin: 10px');
});

it('should have margin style overridden by style prop', async () => {
Expand Down
58 changes: 39 additions & 19 deletions src/core/ActionMenu/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import React, {
import { default as styled } from 'styled-components';
import classnames from 'classnames';
import { AutoId } from '../utils/AutoId/AutoId';
import { SuomifiThemeProp, SuomifiThemeConsumer } from '../theme';
import {
SuomifiThemeProp,
SuomifiThemeConsumer,
SpacingConsumer,
} from '../theme';
import {
ActionMenuPopover,
InitialActiveDescendant,
Expand All @@ -22,14 +26,15 @@ import {
} from '../Button/Button';
import { HtmlDiv } from '../../reset';
import {
spacingStyles,
separateMarginProps,
MarginProps,
GlobalMarginProps,
} from '../theme/utils/spacing';
import { baseStyles } from './ActionMenu.baseStyles';
import { ActionMenuItemProps } from './ActionMenuItem/ActionMenuItem';
import { ActionMenuDividerProps } from './ActionMenuDivider/ActionMenuDivider';
import { IconOptionsVertical } from 'suomifi-icons';
import { filterDuplicateKeys } from '../../utils/common/common';

const baseClassName = 'fi-action-menu';
export const actionMenuClassNames = {
Expand Down Expand Up @@ -106,8 +111,7 @@ const BaseActionMenu = (props: ActionMenuProps) => {
style,
...rest
} = props;
const [marginProps, passProps] = separateMarginProps(rest);
const marginStyle = spacingStyles(marginProps);
const [_marginProps, passProps] = separateMarginProps(rest);

const openButtonRef = forwardedRef || useRef<HTMLButtonElement>(null);
const [menuVisible, setMenuVisible] = useState<boolean>(false);
Expand Down Expand Up @@ -170,7 +174,7 @@ const BaseActionMenu = (props: ActionMenuProps) => {
className={classnames(baseClassName, className, {
[actionMenuClassNames.fullWidth]: fullWidth,
})}
style={{ ...marginStyle, ...style }}
style={style}
>
<Button
id={buttonId}
Expand Down Expand Up @@ -209,31 +213,47 @@ const BaseActionMenu = (props: ActionMenuProps) => {
};

const StyledActionMenu = styled(
({ theme, ...passProps }: ActionMenuProps & SuomifiThemeProp) => (
({
theme,
globalMargins,
...passProps
}: ActionMenuProps & SuomifiThemeProp & GlobalMarginProps) => (
<BaseActionMenu {...passProps} />
),
)`
${() => baseStyles()}
${({ globalMargins, ...rest }) => {
const [marginProps, _passProps] = separateMarginProps(rest);
const cleanedGlobalMargins = filterDuplicateKeys(
globalMargins.actionMenu,
marginProps,
);
return baseStyles(cleanedGlobalMargins, marginProps);
}}
`;

const ActionMenu = forwardRef<HTMLButtonElement, ActionMenuProps>(
(props: ActionMenuProps, ref: React.RefObject<HTMLButtonElement>) => {
const { id: propId, ...passProps } = props;
return (
<SuomifiThemeConsumer>
{({ suomifiTheme }) => (
<AutoId id={propId}>
{(id) => (
<StyledActionMenu
theme={suomifiTheme}
id={id}
forwardedRef={ref}
{...passProps}
/>
<SpacingConsumer>
{({ margins }) => (
<SuomifiThemeConsumer>
{({ suomifiTheme }) => (
<AutoId id={propId}>
{(id) => (
<StyledActionMenu
theme={suomifiTheme}
id={id}
globalMargins={margins}
forwardedRef={ref}
{...passProps}
/>
)}
</AutoId>
)}
</AutoId>
</SuomifiThemeConsumer>
)}
</SuomifiThemeConsumer>
</SpacingConsumer>
);
},
);
Expand Down
13 changes: 9 additions & 4 deletions src/core/Alert/Alert.baseStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import { css } from 'styled-components';
import { SuomifiTheme } from '../theme';
import { element, font } from '../theme/reset';
import { transparentize } from 'polished';
import { MarginProps, buildSpacingCSS } from '../theme/utils/spacing';

export const baseStyles = (theme: SuomifiTheme) => css`
export const baseStyles = (
theme: SuomifiTheme,
globalMargins?: MarginProps,
propMargins?: MarginProps,
) => css`
&.fi-alert {
${element(theme)}
${font(theme)('bodyTextSmall')}
${buildSpacingCSS(globalMargins)}
${buildSpacingCSS(propMargins, true)};
width: 100%;
& .fi-alert_style-wrapper {
Expand All @@ -29,9 +36,7 @@ export const baseStyles = (theme: SuomifiTheme) => css`
height: 40px;
display: inline-block;
padding: 7px;
margin-top: 7px;
margin-right: ${theme.spacing.xs};
margin-bottom: ${theme.spacing.insetM};
margin: 7px ${theme.spacing.xs} ${theme.spacing.insetM} 0;
border: 1px solid transparent;
border-radius: ${theme.radiuses.basic};
white-space: nowrap;
Expand Down
2 changes: 1 addition & 1 deletion src/core/Alert/Alert.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('props', () => {
describe('margin prop', () => {
it('should have margin style from margin prop', () => {
const { container } = render(<Alert closeText="Close" margin="xs" />);
expect(container.firstChild).toHaveAttribute('style', 'margin: 10px;');
expect(container.firstChild).toHaveStyle('margin: 10px');
});

it('should have margin style overwritten from style', () => {
Expand Down
Loading

0 comments on commit 77ccd84

Please sign in to comment.