Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Improving storybook for current widgets and in general #3736

Merged
merged 7 commits into from
Sep 30, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Improving storybook components information
  • Loading branch information
jespino committed Sep 16, 2019
commit 742797804950f208664390de3743f949d70511f1
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import AboutBuildModal from 'components/about_build_modal';

import Menu from 'components/widgets/menu/menu';

import MenuItemBlockableLink from './menu_item_blockable_link';
Copy link
Member Author

Choose a reason for hiding this comment

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

Moving this outside the Menu widget because is too specific to this case, so makes no sense as a general menu entry type.


export default class AdminNavbarDropdown extends React.Component {
static propTypes = {
locale: PropTypes.string.isRequired,
Expand Down Expand Up @@ -50,7 +52,7 @@ export default class AdminNavbarDropdown extends React.Component {

for (const team of teamsArray) {
teamToRender.push(
<Menu.ItemBlockableLink
<MenuItemBlockableLink
key={'team_' + team.name}
to={'/' + team.name}
text={Utils.localizeMessage('navbar_dropdown.switchTo', 'Switch to ') + ' ' + team.display_name}
Expand All @@ -59,7 +61,7 @@ export default class AdminNavbarDropdown extends React.Component {
}
} else {
switchTeams = (
<Menu.ItemBlockableLink
<MenuItemBlockableLink
to={'/select_team'}
icon={
<FormattedMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import PropTypes from 'prop-types';

import BlockableLink from 'components/admin_console/blockable_link';
import menuItem from 'components/widgets/menu/menu_items/menu_item';

export const MenuItemBlockableLinkImpl = ({to, text}) => <BlockableLink to={to}>{text}</BlockableLink>;
MenuItemBlockableLinkImpl.propTypes = {
to: PropTypes.string.isRequired,
text: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
};

const MenuItemBlockableLink = menuItem(MenuItemBlockableLinkImpl);
MenuItemBlockableLink.displayName = 'MenuItemBlockableLinkImpl';

export default MenuItemBlockableLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import {shallow} from 'enzyme';

import {MenuItemBlockableLinkImpl} from './menu_item_blockable_link.jsx';

describe('components/MenuItemBlockableLink', () => {
test('should match snapshot', () => {
const wrapper = shallow(
<MenuItemBlockableLinkImpl
to='/wherever'
text='Whatever'
/>
);

expect(wrapper).toMatchInlineSnapshot(`
<Connect(BlockableLink)
to="/wherever"
>
Whatever
</Connect(BlockableLink)>
`);
});
});
140 changes: 140 additions & 0 deletions components/widgets/admin_console/admin_console.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {useState} from 'react';

import {storiesOf} from '@storybook/react';
import {withKnobs, text, boolean} from '@storybook/addon-knobs';
import {action} from '@storybook/addon-actions';

import AdminPanel from './admin_panel';
import AdminPanelTogglable from './admin_panel_togglable';
import AdminPanelWithButton from './admin_panel_with_button';
import AdminPanelWithLink from './admin_panel_with_link';
import AdminHeader from './admin_header';
import FormattedAdminHeader from './formatted_admin_header';

storiesOf('Admin Console', module).
addDecorator(withKnobs).
add(
'admin panel',
() => {
const content = text('Content', 'Content');
const onHeaderClick = action('clicked header');
const onButtonClick = action('clicked button');
const title = text('Title', 'title');
const subtitle = text('Subtitle', 'subtitle');
const hasButton = boolean('HasButton', false);
return (
<AdminPanel
onHeaderClick={onHeaderClick}
titleId='not-valid-id'
titleDefault={title}
subtitleId='not-valid-id'
subtitleDefault={subtitle}
button={hasButton ? <button onClick={onButtonClick}>{'Button'}</button> : null}
>
{content}
</AdminPanel>
);
}
).
add(
'admin panel with button',
() => {
const content = text('Content', 'Content');
const onHeaderClick = action('clicked header');
const onButtonClick = action('clicked button');
const title = text('Title', 'title');
const subtitle = text('Subtitle', 'subtitle');
const buttonText = text('Button text', 'Button');
const disabled = boolean('Button disabled', false);
return (
<AdminPanelWithButton
onHeaderClick={onHeaderClick}
titleId='not-valid-id'
titleDefault={title}
subtitleId='not-valid-id'
subtitleDefault={subtitle}
buttonTextId='not-valid-id'
buttonTextDefault={buttonText}
onButtonClick={onButtonClick}
disabled={disabled}
>
{content}
</AdminPanelWithButton>
);
}
).
add(
'admin panel with link',
() => {
const content = text('Content', 'Content');
const onHeaderClick = action('clicked header');
const title = text('Title', 'title');
const subtitle = text('Subtitle', 'subtitle');
const linkText = text('Link text', 'Home');
const url = text('Link url', '/');
const disabled = boolean('Button disabled', false);
return (
<AdminPanelWithLink
onHeaderClick={onHeaderClick}
titleId='not-valid-id'
titleDefault={title}
subtitleId='not-valid-id'
subtitleDefault={subtitle}
linkTextId='not-valid-id'
linkTextDefault={linkText}
url={url}
disabled={disabled}
>
{content}
</AdminPanelWithLink>
);
}
).
add(
'togglable admin panel',
() => {
const content = text('Content', 'Content');
const title = text('Title', 'title');
const subtitle = text('Subtitle', 'subtitle');
const Wrapper = () => {
const [open, setOpen] = useState(true);
return (
<AdminPanelTogglable
titleId='not-valid-id'
titleDefault={title}
subtitleId='not-valid-id'
subtitleDefault={subtitle}
open={open}
onToggle={() => setOpen(!open)}
>
{content}
</AdminPanelTogglable>
);
};
return <Wrapper/>;
}
).
add(
'admin header',
() => {
const title = text('Title', 'title');
return (
<AdminHeader>{title}</AdminHeader>
);
}
).
add(
'formatted admin header',
() => {
const markdown = text('Markdown', '**Markdown** text');
return (
<FormattedAdminHeader
id='not-valid-id'
defaultMessage={markdown}
/>
);
}
);
2 changes: 1 addition & 1 deletion components/widgets/badges/badges.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ storiesOf('Badges', module).
add(
'regular badge',
() => {
const content = text('Text', 'Text');
const content = text('Text', 'BADGE');
return (<Badge show={boolean('Show', true)}>{content}</Badge>);
}
).
Expand Down
107 changes: 107 additions & 0 deletions components/widgets/inputs/inputs.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {useState} from 'react';

import {storiesOf} from '@storybook/react';
import {withKnobs, text} from '@storybook/addon-knobs';

import ChannelsInput from './channels_input';
import UsersEmailsInput from './users_emails_input';

storiesOf('Inputs', module).
addDecorator(withKnobs).
add(
'channels input',
() => {
const WrapperComponent = () => {
const placeholder = text('Placeholder', 'Placeholder');
const loadingMessageDefault = text('Loading Message', 'Loading');
const noOptionsMessageDefault = text('No Options Message', 'No channels found');
const options = [
{id: '1', name: 'public', display_name: 'Public Channel', type: 'O'},
{id: '2', name: 'private', display_name: 'Private Channel', type: 'P'},
{id: '3', name: 'town-square', display_name: 'Town Square', type: 'O'},
{id: '4', name: 'off-topic', display_name: 'Off Topic', type: 'O'},
];

const [value, setValue] = useState([]);
const [inputValue, setInputValue] = useState('');

const channelsLoader = (input, callback) => {
const values = options.filter((channel) => channel.display_name.toLowerCase().startsWith(input.toLowerCase()));
setTimeout(() => callback(values), 500);
};

return (
<ChannelsInput
channelsLoader={channelsLoader}
placeholder={placeholder}
onChange={setValue}
value={value}
onInputChange={setInputValue}
inputValue={inputValue}
loadingMessageDefault={loadingMessageDefault}
loadingMessageId='not-existing-id'
noOptionsMessageDefault={noOptionsMessageDefault}
noOptionsMessageId='not-existing-id'
/>
);
};
return (
<WrapperComponent/>
);
}
).
add(
'users emails input',
() => {
const WrapperComponent = () => {
const placeholder = text('Placeholder', 'Placeholder');
const loadingMessageDefault = text('Loading Message', 'Loading');
const noMatchMessageDefault = text('No Match Message', 'No one found matching **{text}**, type email address');
const validAddressMessageDefault = text('Valid Address', 'Add **{email}**');
const options = [
{id: '1', username: 'jesus.espino', first_name: 'Jesús', last_name: 'Espino', nickname: 'jespino'},
{id: '2', username: 'jora.wilander', first_name: 'Joram', last_name: 'Wilander', nickname: 'jwilander'},
{id: '3', username: 'ben.schumaher', first_name: 'Ben', last_name: 'Schumacher', nickname: 'Hanzei'},
{id: '4', username: 'martin.kraft', first_name: 'Martin', last_name: 'Kraft', nickname: 'mkraft'},
];

const [value, setValue] = useState([]);
const [inputValue, setInputValue] = useState('');

const usersLoader = (input, callback) => {
const values = options.filter((user) => {
return (
user.first_name.toLowerCase().startsWith(input.toLowerCase()) ||
user.last_name.toLowerCase().startsWith(input.toLowerCase()) ||
user.username.toLowerCase().startsWith(input.toLowerCase()) ||
user.nickname.toLowerCase().startsWith(input.toLowerCase())
);
});
setTimeout(() => callback(values), 500);
};

return (
<UsersEmailsInput
usersLoader={usersLoader}
placeholder={placeholder}
onChange={setValue}
value={value}
onInputChange={setInputValue}
inputValue={inputValue}
loadingMessageDefault={loadingMessageDefault}
loadingMessageId='not-existing-id'
noMatchMessageDefault={noMatchMessageDefault}
noMatchMessageId='not-existing-id'
validAddressMessageDefault={validAddressMessageDefault}
validAddressMessageId='not-existing-id'
/>
);
};
return (
<WrapperComponent/>
);
}
);
31 changes: 6 additions & 25 deletions components/widgets/loading/loading.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,11 @@ import LoadingWrapper from './loading_wrapper';

storiesOf('Loading', module).
addDecorator(withKnobs).
add('loadingSpinner',
() => {
return (
<div>
<h2>{'Loading spinner without text'}</h2>
<LoadingSpinner/>
<h2>{'Loading spinner without text'}</h2>
<LoadingSpinner text={text('Text', 'Loading')}/>
</div>
);
},
{
notes: {markdown: 'Loading spinner'},
}
add('LoadingSpinner without text',
() => (<LoadingSpinner/>),
).
add('LoadingSpinner with text',
() => <LoadingSpinner text={text('Text', 'Loading')}/>
).
add('loadingWrapper', () => {
const LoadingExample = () => {
Expand All @@ -51,15 +42,5 @@ storiesOf('Loading', module).
);
};

const content = text('Content', 'Content');
return (
<div>
<h2>{'Wrapped contend not loading'}</h2>
<LoadingWrapper loading={false}>{content}</LoadingWrapper>
<h2>{'Wrapped contend loading'}</h2>
<LoadingWrapper loading={true}>{content}</LoadingWrapper>
<h2>{'Sample'}</h2>
<LoadingExample/>
</div>
);
return (<LoadingExample/>);
});
4 changes: 2 additions & 2 deletions components/widgets/menu/menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import {isMobile} from 'utils/utils';

import MenuGroup from './menu_group';
import MenuItemAction from './menu_items/menu_item_action';
import MenuItemBlockableLink from './menu_items/menu_item_blockable_link';
import MenuItemExternalLink from './menu_items/menu_item_external_link';
import MenuItemLink from './menu_items/menu_item_link';
import MenuItemToggleModalRedux from './menu_items/menu_item_toggle_modal_redux';

import './menu.scss';

export default class Menu extends React.PureComponent {
static Group = MenuGroup
static ItemAction = MenuItemAction
static ItemBlockableLink = MenuItemBlockableLink
static ItemExternalLink = MenuItemExternalLink
static ItemLink = MenuItemLink
static ItemToggleModalRedux = MenuItemToggleModalRedux
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
@import 'compass/utilities';
@import 'compass/css3';

.Menu {
z-index: 10000;

Expand Down
Loading