fbredius/storybook

View on GitHub
lib/ui/src/components/notifications/NotificationItem.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import React, { FunctionComponent, SyntheticEvent } from 'react';
import { State } from '@storybook/api';
import { Link } from '@storybook/router';
import { styled } from '@storybook/theming';
import { Icons, IconButton, IconsProps } from '@storybook/components';
import { transparentize } from 'polished';

const DEFAULT_ICON_COLOUR = '#66BF3C' as const;

const Notification = styled.div(({ theme }) => ({
  position: 'relative',
  display: 'flex',
  padding: 15,
  width: 280,
  borderRadius: 4,
  alignItems: 'center',

  background:
    theme.base === 'light'
      ? 'rgba(50,53,71,0.97)'
      : 'linear-gradient(0deg, rgba(248,248,248,0.97) 0%, rgba(247,252,255,0.97) 100%)',
  boxShadow: `0 2px 5px 0 rgba(0,0,0,0.05), 0 5px 15px 0 rgba(0,0,0,0.1)`,
  color: theme.color.inverseText,
  textDecoration: 'none',
}));

const NotificationWithInteractiveStates = styled(Notification)(() => ({
  transition: 'all 150ms ease-out',
  transform: 'translate3d(0, 0, 0)',
  '&:hover': {
    transform: 'translate3d(0, -3px, 0)',
    boxShadow:
      '0 1px 3px 0 rgba(30,167,253,0.5), 0 2px 5px 0 rgba(0,0,0,0.05), 0 5px 15px 0 rgba(0,0,0,0.1)',
  },
  '&:active': {
    transform: 'translate3d(0, 0, 0)',
    boxShadow:
      '0 1px 3px 0 rgba(30,167,253,0.5), 0 2px 5px 0 rgba(0,0,0,0.05), 0 5px 15px 0 rgba(0,0,0,0.1)',
  },
  '&:focus': {
    boxShadow:
      '0 1px 3px 0 rgba(30,167,253,0.5), 0 2px 5px 0 rgba(0,0,0,0.05), 0 5px 15px 0 rgba(0,0,0,0.1)',
  },
}));
const NotificationLink = NotificationWithInteractiveStates.withComponent(Link);

const NotificationIconWrapper = styled.div(() => ({
  display: 'flex',
  marginRight: 10,
  alignItems: 'center',
}));

const NotificationTextWrapper = styled.div(() => ({
  width: '100%',
  display: 'flex',
  flexDirection: 'column',
}));

const Headline = styled.div<{ hasIcon: boolean }>(({ theme, hasIcon }) => ({
  height: '100%',
  width: hasIcon ? 205 : 230,
  alignItems: 'center',
  whiteSpace: 'nowrap',
  overflow: 'hidden',
  textOverflow: 'ellipsis',
  fontSize: theme.typography.size.s1,
  lineHeight: '16px',
  fontWeight: theme.typography.weight.bold,
}));

const SubHeadline = styled.div(({ theme }) => ({
  color: transparentize(0.25, theme.color.inverseText),
  fontSize: theme.typography.size.s1 - 1,
  lineHeight: '14px',
  marginTop: 2,
}));

const ItemContent: FunctionComponent<Pick<State['notifications'][0], 'icon' | 'content'>> = ({
  icon,
  content: { headline, subHeadline },
}) => (
  <>
    {!icon || (
      <NotificationIconWrapper>
        <Icons
          icon={icon.name as IconsProps['icon']}
          width={16}
          color={icon.color || DEFAULT_ICON_COLOUR}
        />
      </NotificationIconWrapper>
    )}
    <NotificationTextWrapper>
      <Headline title={headline} hasIcon={!!icon}>
        {headline}
      </Headline>
      {subHeadline && <SubHeadline>{subHeadline}</SubHeadline>}
    </NotificationTextWrapper>
  </>
);

const DismissButtonWrapper = styled(IconButton)(({ theme }) => ({
  alignSelf: 'center',
  marginTop: 0,
  color: theme.base === 'light' ? 'rgba(255,255,255,0.7)' : ' #999999',
}));

const DismissNotificationItem: FunctionComponent<{
  onDismiss: () => void;
}> = ({ onDismiss }) => (
  <DismissButtonWrapper
    title="Dismiss notification"
    onClick={(e: SyntheticEvent) => {
      e.preventDefault();
      onDismiss();
    }}
  >
    <Icons icon="closeAlt" height={12} width={12} />
  </DismissButtonWrapper>
);

export const NotificationItemSpacer = styled.div({
  height: 48,
});

const NotificationItem: FunctionComponent<{
  notification: State['notifications'][0];
  onDismissNotification: (id: string) => void;
}> = ({ notification: { content, link, onClear, id, icon }, onDismissNotification }) => {
  const dismissNotificationItem = () => {
    onDismissNotification(id);
    onClear();
  };
  return link ? (
    <NotificationLink to={link}>
      <ItemContent icon={icon} content={content} />
      <DismissNotificationItem onDismiss={dismissNotificationItem} />
    </NotificationLink>
  ) : (
    <Notification>
      <ItemContent icon={icon} content={content} />
      <DismissNotificationItem onDismiss={dismissNotificationItem} />
    </Notification>
  );
};

export default NotificationItem;