Unverified Commit 7c7c8e92 authored by Marcos Iglesias's avatar Marcos Iglesias Committed by GitHub

chore: Updating Storybook with Cards, Colors and Typography (#620)

Signed-off-by: 's avatarMarcos Iglesias Valle <golodhros@gmail.com>
parent 8b9ce930
......@@ -6,6 +6,9 @@
$icon-size: 24px;
$icon-small-size: 16px;
// Icons
// Lookout! When you update one of these, please update the enums on
// /static/js/interfaces/Enums.ts
// Map of Database names and icon paths
$data-stores: (
database: '/static/images/icons/Database.svg',
......
......@@ -4,28 +4,23 @@
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { RouteComponentProps } from 'react-router';
import * as qs from 'simple-query-string';
import * as ReactMarkdown from 'react-markdown';
import AvatarLabel from 'components/common/AvatarLabel';
import { getDashboard } from 'ducks/dashboard/reducer';
import { GetDashboardRequest } from 'ducks/dashboard/types';
import { GlobalState } from 'ducks/rootReducer';
import { logClick } from 'ducks/utilMethods';
import Breadcrumb from 'components/common/Breadcrumb';
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import EditableSection from 'components/common/EditableSection';
import LoadingSpinner from 'components/common/LoadingSpinner';
import TabsComponent from 'components/common/TabsComponent';
import { getDashboard } from 'ducks/dashboard/reducer';
import { GetDashboardRequest } from 'ducks/dashboard/types';
import { GlobalState } from 'ducks/rootReducer';
import { logClick } from 'ducks/utilMethods';
import { DashboardMetadata } from 'interfaces/Dashboard';
import DashboardOwnerEditor from 'components/DashboardPage/DashboardOwnerEditor';
import QueryList from 'components/DashboardPage/QueryList';
import ChartList from 'components/DashboardPage/ChartList';
import ResourceStatusMarker from 'components/common/ResourceStatusMarker';
import { formatDateTimeShort } from 'utils/dateUtils';
import ResourceList from 'components/common/ResourceList';
import {
ADD_DESC_TEXT,
......@@ -37,14 +32,15 @@ import {
STATUS_TEXT,
} from 'components/DashboardPage/constants';
import TagInput from 'components/common/Tags/TagInput';
import { ResourceType } from 'interfaces';
import { NO_TIMESTAMP_TEXT } from 'components/constants';
import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
import { BadgeStyle } from 'config/config-types';
import { formatDateTimeShort } from 'utils/dateUtils';
import { getLoggingParams } from 'utils/logUtils';
import { NO_TIMESTAMP_TEXT } from 'components/constants';
import { ResourceType } from 'interfaces';
import { DashboardMetadata } from 'interfaces/Dashboard';
import ImagePreview from './ImagePreview';
import './styles.scss';
......
......@@ -3,9 +3,6 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import FlashMessage from 'components/common/FlashMessage';
import globalState from 'fixtures/globalState';
import {
NotificationType,
......
......@@ -16,12 +16,12 @@ import {
} from 'interfaces';
import FlashMessage from 'components/common/FlashMessage';
import { ImageIconType } from 'interfaces/Enums';
import { GlobalState } from 'ducks/rootReducer';
import {
CloseRequestAction,
OpenRequestAction,
SubmitNotificationRequest,
} from 'ducks/notification/types';
import {
......@@ -84,7 +84,7 @@ export class RequestMetadataForm extends React.Component<
renderFlashMessage = () => {
return (
<FlashMessage
iconClass="icon-mail"
iconClass={ImageIconType.MAIL}
message={this.getFlashMessageString()}
onClose={this.closeDialog}
/>
......
import React from 'react';
import { storiesOf } from '@storybook/react';
import StorySection from '../StorySection';
import Card from '.';
const stories = storiesOf('Components/Cards', module);
stories.add('Cards', () => (
<>
<StorySection title="Loading Card">
<Card isLoading />
</StorySection>
<StorySection title="Basic Card">
<Card
title="Card Title"
copy="Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima autem dolorum incidunt quaerat perspiciatis! Totam est, ab molestiae magnam quisquam eligendi enim eum, iste excepturi mollitia laboriosam cumque, vitae reiciendis."
/>
</StorySection>
<StorySection title="Full Card">
<Card
title="Card Title"
subtitle="Card Subtitle"
copy="Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima autem dolorum incidunt quaerat perspiciatis! Totam est, ab molestiae magnam quisquam eligendi enim eum, iste excepturi mollitia laboriosam cumque, vitae reiciendis."
/>
</StorySection>
</>
));
import React from 'react';
import { storiesOf } from '@storybook/react';
import { ImageIconType } from 'interfaces/Enums';
import StorySection from '../StorySection';
import FlashMessage from '.';
const stories = storiesOf('Components/Flash Message', module);
stories.add('Flash Message', () => (
<>
<StorySection title="Flash Message">
<FlashMessage
message="Flash message text that can be short"
onClose={() => {
alert('message closed!');
}}
/>
</StorySection>
<StorySection title="Flash Message with Icon">
<FlashMessage
message="Flash message text that can be short"
iconClass={ImageIconType.ALERT}
onClose={() => {
alert('message closed!');
}}
/>
</StorySection>
</>
));
......@@ -5,6 +5,7 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import { ImageIconType } from 'interfaces/Enums';
import FlashMessage, { FlashMessageProps } from '.';
describe('FlashMessage', () => {
......@@ -22,20 +23,23 @@ describe('FlashMessage', () => {
describe('render', () => {
let props: FlashMessageProps;
let wrapper;
beforeAll(() => {
const setupResult = setup();
props = setupResult.props;
wrapper = setupResult.wrapper;
({ props, wrapper } = setup());
});
describe('iconClass logic', () => {
it('if no iconClass, does not render img', () => {
expect(wrapper.find('img').exists()).toBe(false);
const expected = 0;
const actual = wrapper.find('img').length;
expect(actual).toEqual(expected);
});
it('if iconClass, renders img with correct className', () => {
const testClass = 'icon-mail';
const testClass = ImageIconType.MAIL;
const { wrapper } = setup({ iconClass: testClass });
expect(wrapper.find('img').props()).toMatchObject({
className: `icon ${testClass}`,
});
......@@ -43,13 +47,17 @@ describe('FlashMessage', () => {
});
it('renders correct message text', () => {
expect(wrapper.find('div.message').text()).toBe(props.message);
const expected = props.message;
const actual = wrapper.find('.message').text();
expect(actual).toEqual(expected);
});
it('renders correct button', () => {
expect(wrapper.find('button.btn.btn-close').props().onClick).toBe(
props.onClose
);
const expected = props.onClose;
const actual = wrapper.find('button.btn.btn-close').props().onClick;
expect(actual).toEqual(expected);
});
});
});
......@@ -2,12 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
import * as React from 'react';
import { ImageIconType } from 'interfaces/Enums';
import * as Constants from './constants';
import './styles.scss';
export interface FlashMessageProps {
iconClass?: string | null;
iconClass?: ImageIconType | null;
message: string;
onClose: (event: React.MouseEvent<HTMLButtonElement>) => void;
}
......@@ -19,8 +20,10 @@ const FlashMessage: React.FC<FlashMessageProps> = ({
}: FlashMessageProps) => {
return (
<div className="flash-message">
<div>
{iconClass && <img className={`icon ${iconClass}`} alt="" />}
<div className="message">{message}</div>
<p className="message">{message}</p>
</div>
<button type="button" className="btn btn-close" onClick={onClose}>
<span className="sr-only">{Constants.CLOSE}</span>
</button>
......
......@@ -2,22 +2,35 @@
// SPDX-License-Identifier: Apache-2.0
@import 'variables';
@import 'typography';
$flash-message-height: 56px;
$flash-message-border-radius: 4px;
$flash-message-message-line-height: 24px;
.flash-message {
background-color: $body-bg-dark;
border-radius: 6px;
border-radius: $flash-message-border-radius;
color: $white;
display: flex;
height: 56px;
padding: $spacer-2 $spacer-1;
height: $flash-message-height;
padding: $spacer-2;
padding-right: $spacer-1 * 1.5;
justify-content: space-between;
.message {
line-height: 24px;
margin-right: $spacer-2;
@extend %text-body-w3;
line-height: $flash-message-message-line-height;
margin: 0;
display: inline;
}
.icon {
margin: 0 $spacer-1 0 0;
}
.icon,
.btn-close {
margin: auto $spacer-1;
margin: auto 0 auto $spacer-3;
}
}
import React, { ReactNode } from 'react';
type BlockProps = {
children: ReactNode;
title: string;
text?: string | ReactNode;
};
const StorySection: React.FC<BlockProps> = ({
children,
text,
title,
}: BlockProps) => (
<div style={{ padding: '2em', maxWidth: 600 }}>
<h1 className="text-headline-w1">{title}</h1>
{text && <p className="text-body-w1">{text}</p>}
{children}
</div>
);
export default StorySection;
......@@ -17,3 +17,47 @@ export enum SearchType {
PAGINATION = 'update_page',
SUBMIT_TERM = 'submit_search_term',
}
// Image-based Icon types from _icons.scss
export enum ImageIconType {
ALERT = 'icon-alert',
BOOKMARK = 'icon-bookmark',
BOOKMARK_FILLED = 'icon-bookmark-filled',
DELETE = 'icon-delete',
RED_TRIANGLE_WARNING = 'icon-red-triangle-warning',
DOWN = 'icon-down',
EDIT = 'icon-edit',
HELP = 'icon-help',
GITHUB = 'icon-github',
LEFT = 'icon-left',
LOADING = 'icon-loading',
MAIL = 'icon-mail',
PLUS = 'icon-plus',
PLUS_CIRCLE = 'icon-plus-circle',
PREVIEW = 'icon-preview',
REFRESH = 'icon-refresh',
RIGHT = 'icon-right',
SEARCH = 'icon-search',
SEND = 'icon-send',
SLACK = 'icon-slack',
UP = 'icon-up',
USER = 'icon-user',
MORE = 'icon-more',
}
// Icon types from _icons.scss
export enum IconType {
CHECK = 'icon-check',
USERS = 'icon-users',
DASHBOARD = 'icon-dashboard',
MODE = 'icon-mode',
REDASH = 'icon-redash',
TABLEAU = 'icon-tableau',
DATABASE = 'icon-database',
HIVE = 'icon-hive',
BIGQUERY = 'icon-bigquery',
DRUID = 'icon-druid',
PRESTO = 'icon-presto',
POSTGRES = 'icon-postgres',
REDSHIFT = 'icon-redshift',
}
import React from 'react';
import { storiesOf } from '@storybook/react';
import StorySection from '../components/common/StorySection';
const stories = storiesOf('Attributes/Colors', module);
const COLOR_BLOCK_SIZE = '80px';
type ColorBlockProps = {
color: string;
title: string;
};
const ColorBlock: React.FC<ColorBlockProps> = ({
color,
title,
}: ColorBlockProps) => (
<div style={{ margin: '20px' }}>
<h3 className="text-subtitle-w2">{title}</h3>
<div
style={{
width: COLOR_BLOCK_SIZE,
height: COLOR_BLOCK_SIZE,
backgroundColor: `${color}`,
borderRadius: '3px',
}}
/>
</div>
);
stories.add('Colors', () => (
<>
<StorySection title="Brand Colors">
<div style={{ display: 'flex' }}>
<ColorBlock title="$brand-color-1" color="#DCDCFF" />
<ColorBlock title="$brand-color-2" color="#BABAFF" />
<ColorBlock title="$brand-color-3" color="#8481FF" />
<ColorBlock title="$brand-color-4" color="#8481FF" />
<ColorBlock title="$brand-color-5" color="#523BE4" />
</div>
</StorySection>
<StorySection title="Text Colors">
<div style={{ display: 'flex' }}>
<ColorBlock title="$text-primary" color="#292936" />
<ColorBlock title="$text-secondary" color="#292936" />
<ColorBlock title="$text-tertiary" color="#9191A8" />
<ColorBlock title="$text-placeholder" color="#9191A8" />
<ColorBlock title="$text-inverse" color="#FFFFFF" />
</div>
</StorySection>
</>
));
import React from 'react';
import React, { ReactNode } from 'react';
import StorySection from '../components/common/StorySection';
export default {
title: 'Attributes/Typography',
};
export const TypographyUpdated = () => {
return (
<>
<StorySection title="Headlines">
<>
<h1 className="text-headline-w1">Heading with .text-headline-w1</h1>
<h1 className="text-headline-w2">Heading with .text-headline-w2</h1>
<h1 className="text-headline-w3">Heading with .text-headline-w3</h1>
</>
</StorySection>
<StorySection title="Titles">
<>
<h1 className="text-title-w1">Title with .text-title-w1</h1>
<h1 className="text-title-w2">Title with .text-title-w2</h1>
<h1 className="text-title-w3">Title with .text-title-w3</h1>
</>
</StorySection>
<StorySection title="Subtitles">
<>
<h1 className="text-subtitle-w1">Subtitle with .text-subtitle-w1</h1>
<h1 className="text-subtitle-w2">Subtitle with .text-subtitle-w2</h1>
<h1 className="text-subtitle-w3">Subtitle with .text-subtitle-w3</h1>
</>
</StorySection>
<StorySection title="Body Text">
<>
<p className="text-body-w1">Subtitle with .text-body-w1</p>
<p className="text-body-w2">Subtitle with .text-body-w2</p>
<p className="text-body-w3">Subtitle with .text-body-w3</p>
</>
</StorySection>
</>
);
};
TypographyUpdated.story = {
name: 'Typography',
};
export const Typography = () => {
return (
<>
<h1>Headings</h1>
<hr />
<StorySection title="Headings">
<>
<h1>Raw h1</h1>
<h2>Raw h2</h2>
<h3>Raw h3</h3>
......@@ -23,18 +64,9 @@ export const Typography = () => {
<h1 className="subtitle-2">Heading with .subtitle-2</h1>
<h1 className="subtitle-3">Heading with .subtitle-3</h1>
</>
);
};
Typography.story = {
name: 'Headings',
};
export const Body = () => {
return (
</StorySection>
<StorySection title="Body Text">
<>
<h1>Body</h1>
<hr />
<p>Raw p</p>
<hr />
<p className="body-1">Paragraph with .body-1</p>
......@@ -51,9 +83,11 @@ export const Body = () => {
<p className="text-secondary">Paragraph with .text-secondary</p>
<p className="text-primary">Paragraph with .text-primary</p>
</>
</StorySection>
</>
);
};
Body.story = {
name: 'Body Text',
Typography.story = {
name: 'Deprecated: Headings & Body',
};
import React from 'react';
import { Welcome } from '@storybook/react/demo';
import StorySection from '../components/common/StorySection';
export default {
title: 'Overview/Introduction',
component: Welcome,
......@@ -9,11 +11,15 @@ export default {
export const Introduction = () => {
return (
<>
<h1>Welcome to Amundsen's Component Library!</h1>
<h3>
<StorySection
title="Welcome to Amundsen's Component Library!"
text={
<h3 className="text-subtitle-w2">
A development area for developing new{' '}
<strong>presentational components</strong>
</h3>
}
>
<p>
Do you ever miss having a “workshop” to develop new components before
hooking them with the real data? Look no more, here is the place!
......@@ -34,6 +40,7 @@ export const Introduction = () => {
</li>
<li>Prototype something really quick to show around</li>
</ul>
</StorySection>
</>
);
};
......@@ -25,7 +25,7 @@
"stylelint": "stylelint '**/*.scss'",
"stylelint:fix": "stylelint --fix '**/*.scss'",
"format": "prettier --loglevel warn --write \"**/*.{ts,tsx,css,scss}\"",
"storybook": "cross-env TS_NODE_PROJECT='tsconfig.webpack.json' start-storybook -p 6006",
"storybook": "cross-env TS_NODE_PROJECT='tsconfig.webpack.json' start-storybook -s ../ -p 6006",
"build-storybook": "cross-env TS_NODE_PROJECT='tsconfig.webpack.json' build-storybook",
"betterer": "betterer",
"betterer:update": "betterer --update"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment