refactor(stories): co-locate stories with components (#2078)

* refactor: move ActionButton component

* refactor: move BanUserButton component

* refactor: move ChatActionMessage component

* refactor: move ChatContainer component

* refactor: move AuthModal component

* refactor: move BrowserNotifyModal component

* refactor: move ChatUserMessage component

* refactor: move ChatJoinMessage component

* refactor: move ChatTextField component

* refactor: move ChatUserBadge component

* refactor: move FollowerCollection and SingleFollower components

* fix: bad import path

* refactor: move FollowModal component

* refactor: move Modal component

* refactor: move ContentHeader component

* refactor: move ChatSystemMessage component

* refactor: move Header component

* refactor: move Footer component

* refactor: move StatusBar component

* refactor: move OfflineBanner component

* refactor: move OwncastPlayer component

* refactor: move IndieAuthModal component

* refactor: move SocialLinks component

* refactor: move VideoPoster component

* refactor: move FollowModal component

* refactor: move FediAuthModal.tsx component

* refactor: move UserDropdown component

* refactor: move ChatSocialMessage component

* refactor: move Logo component

* refactor: move NotifyReminderPopup component

* refactor: move NameChangeModal component

* refactor: move FatalErrorStateModal component

* refactor: move ChatModeratorNotification component

* refactor: move ChatModerationActionMenu and ChatModerationDetailsModal components

* refactor: move CustomPageContent component

* refactor: move storybook Introduction file

* refactor: update storybook story import path

* refactor: move storybook preview styles

* refactor: move storybook doc pages

* refactor: move Color and ImageAsset components

* fix: bad import path

* fix: bad import path in story file
This commit is contained in:
James Young
2022-09-03 20:38:52 +02:00
committed by GitHub
parent 9f550a87d2
commit 5ebbbb8bf2
82 changed files with 120 additions and 121 deletions

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatActionMessage from './ChatActionMessage';
import Mock from '../../../stories/assets/mocks/chatmessage-action.png';
export default {
title: 'owncast/Chat/Messages/Chat action',
component: ChatActionMessage,
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `This is the message design an action takes place, such as a join or a name change.`,
},
},
},
} as ComponentMeta<typeof ChatActionMessage>;
const Template: ComponentStory<typeof ChatActionMessage> = args => <ChatActionMessage {...args} />;
export const Basic = Template.bind({});
Basic.args = {
body: 'This is a basic action message.',
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatJoinMessage from './ChatJoinMessage';
import Mock from '../../../stories/assets/mocks/chatmessage-action.png';
export default {
title: 'owncast/Chat/Messages/Chat Join',
component: ChatJoinMessage,
argTypes: {
userColor: {
options: ['0', '1', '2', '3', '4', '5', '6', '7'],
control: { type: 'select' },
},
},
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `This is the message design an action takes place, such as a join or a name change.`,
},
},
},
} as ComponentMeta<typeof ChatJoinMessage>;
const Template: ComponentStory<typeof ChatJoinMessage> = args => <ChatJoinMessage {...args} />;
export const Regular = Template.bind({});
Regular.args = {
displayName: 'RandomChatter',
isAuthorModerator: false,
userColor: 3,
};
export const Moderator = Template.bind({});
Moderator.args = {
displayName: 'RandomChatter',
isAuthorModerator: true,
userColor: 2,
};

View File

@@ -0,0 +1,97 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import ChatModerationActionMenu from './ChatModerationActionMenu';
const mocks = {
mocks: [
{
// The "matcher" determines if this
// mock should respond to the current
// call to fetch().
matcher: {
name: 'response',
url: 'glob:/api/moderation/chat/user/*',
},
// If the "matcher" matches the current
// fetch() call, the fetch response is
// built using this "response".
response: {
status: 200,
body: {
user: {
id: 'hjFPU967R',
displayName: 'focused-snyder',
displayColor: 2,
createdAt: '2022-07-12T13:08:31.406505322-07:00',
previousNames: ['focused-snyder'],
scopes: ['MODERATOR'],
isBot: false,
authenticated: false,
},
connectedClients: [
{
messageCount: 3,
userAgent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
connectedAt: '2022-07-20T16:45:07.796685618-07:00',
geo: 'N/A',
},
],
messages: [
{
id: 'bQp8UJR4R',
timestamp: '2022-07-20T16:53:41.938083228-07:00',
user: null,
body: 'test message 3',
},
{
id: 'ubK88Jg4R',
timestamp: '2022-07-20T16:53:39.675531279-07:00',
user: null,
body: 'test message 2',
},
{
id: '20v8UJRVR',
timestamp: '2022-07-20T16:53:37.551084121-07:00',
user: null,
body: 'test message 1',
},
],
},
},
},
],
};
export default {
title: 'owncast/Chat/Moderation menu',
component: ChatModerationActionMenu,
parameters: {
fetchMock: mocks,
docs: {
description: {
component: `This should be a popup that is activated from a user's chat message. It should have actions to:
- Remove single message
- Ban user completely
- Open modal to see user details
`,
},
},
},
} as ComponentMeta<typeof ChatModerationActionMenu>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof ChatModerationActionMenu> = args => (
<RecoilRoot>
<ChatModerationActionMenu
accessToken="abc123"
messageID="xxx"
userDisplayName="Fake-User"
userID="abc123"
/>
</RecoilRoot>
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Basic = Template.bind({});

View File

@@ -6,7 +6,7 @@ import {
} from '@ant-design/icons';
import { Dropdown, Menu, MenuProps, Space, Modal, message } from 'antd';
import { useState } from 'react';
import ChatModerationDetailsModal from './ChatModerationDetailsModal';
import ChatModerationDetailsModal from '../ChatModerationDetailsModal/ChatModerationDetailsModal';
import s from './ChatModerationActionMenu.module.scss';
import ChatModeration from '../../../services/moderation-service';

View File

@@ -0,0 +1,92 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import ChatModerationDetailsModal from './ChatModerationDetailsModal';
const mocks = {
mocks: [
{
// The "matcher" determines if this
// mock should respond to the current
// call to fetch().
matcher: {
name: 'response',
url: 'glob:/api/moderation/chat/user/*',
},
// If the "matcher" matches the current
// fetch() call, the fetch response is
// built using this "response".
response: {
status: 200,
body: {
user: {
id: 'hjFPU967R',
displayName: 'focused-snyder',
displayColor: 2,
createdAt: '2022-07-12T13:08:31.406505322-07:00',
previousNames: ['focused-snyder'],
scopes: ['MODERATOR'],
isBot: false,
authenticated: false,
},
connectedClients: [
{
messageCount: 3,
userAgent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
connectedAt: '2022-07-20T16:45:07.796685618-07:00',
geo: 'N/A',
},
],
messages: [
{
id: 'bQp8UJR4R',
timestamp: '2022-07-20T16:53:41.938083228-07:00',
user: null,
body: 'test message 3',
},
{
id: 'ubK88Jg4R',
timestamp: '2022-07-20T16:53:39.675531279-07:00',
user: null,
body: 'test message 2',
},
{
id: '20v8UJRVR',
timestamp: '2022-07-20T16:53:37.551084121-07:00',
user: null,
body: 'test message 1',
},
],
},
},
},
],
};
export default {
title: 'owncast/Chat/Moderation modal',
component: ChatModerationDetailsModal,
parameters: {
fetchMock: mocks,
docs: {
description: {
component: `This should be a modal that gives the moderator more details about the user such as:
- When the user was created
- Other names they've used
- If they're authenticated, and using what method (IndieAuth, FediAuth)
`,
},
},
},
} as ComponentMeta<typeof ChatModerationDetailsModal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof ChatModerationDetailsModal> = args => (
<RecoilRoot>
<ChatModerationDetailsModal userId="testuser123" accessToken="fakeaccesstoken4839" />
</RecoilRoot>
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Example = Template.bind({});

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatModeratorNotification from './ChatModeratorNotification';
export default {
title: 'owncast/Chat/Messages/Moderation Role Notification',
component: ChatModeratorNotification,
parameters: {},
} as ComponentMeta<typeof ChatModeratorNotification>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof ChatModeratorNotification> = (args: object) => (
<ChatModeratorNotification {...args} />
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Basic = Template.bind({});

View File

@@ -0,0 +1,15 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatSocialMessage from './ChatSocialMessage';
export default {
title: 'owncast/Chat/Messages/Social-fediverse event',
component: ChatSocialMessage,
parameters: {},
} as ComponentMeta<typeof ChatSocialMessage>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof ChatSocialMessage> = args => <ChatSocialMessage {...args} />;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Basic = Template.bind({});

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/no-unused-prop-types */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ChatMessage } from '../../interfaces/chat-message.model';
import { ChatMessage } from '../../../interfaces/chat-message.model';
interface Props {
message: ChatMessage;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatSystemMessage from './ChatSystemMessage';
import Mock from '../../../stories/assets/mocks/chatmessage-system.png';
import { ChatMessage } from '../../../interfaces/chat-message.model';
export default {
title: 'owncast/Chat/Messages/System',
component: ChatSystemMessage,
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `This is the message design used when the server sends a message to chat.`,
},
},
},
} as ComponentMeta<typeof ChatSystemMessage>;
const Template: ComponentStory<typeof ChatSystemMessage> = args => <ChatSystemMessage {...args} />;
const message: ChatMessage = JSON.parse(`{
"type": "SYSTEM",
"id": "wY-MEXwnR",
"timestamp": "2022-04-28T20:30:27.001762726Z",
"user": {
"id": "h_5GQ6E7R",
"displayName": "Cool Server Name",
"createdAt": "2022-03-24T03:52:37.966584694Z",
"scopes": []
},
"body": "Test system message from the chat server."}`);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Basic = Template.bind({});
Basic.args = {
message,
};
export const HighlightExample = Template.bind({});
HighlightExample.args = {
message,
highlightString: 'chat',
};

View File

@@ -0,0 +1,49 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import ChatTextField from './ChatTextField';
import Mock from '../../../stories/assets/mocks/chatinput-mock.png';
export default {
title: 'owncast/Chat/Input text field',
component: ChatTextField,
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `
- This is a element using \`contentEditable\` in order to support rendering emoji images inline.
- Emoji button shows emoji picker.
- Should show one line by default, but grow to two lines as needed.
- The Send button should be hidden for desktop layouts and be shown for mobile layouts.`,
},
},
},
} as ComponentMeta<typeof ChatTextField>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof ChatTextField> = args => (
<RecoilRoot>
<ChatTextField {...args} />
</RecoilRoot>
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Example = Template.bind({});
export const LongerMessage = Template.bind({});
LongerMessage.args = {
value:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
};
LongerMessage.parameters = {
docs: {
description: {
story: 'Should display two lines of text and scroll to display more.',
},
},
};

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatUserBadge from './ChatUserBadge';
export default {
title: 'owncast/Chat/Messages/User Flag',
component: ChatUserBadge,
argTypes: {
userColor: {
options: ['0', '1', '2', '3', '4', '5', '6', '7'],
control: { type: 'select' },
},
},
} as ComponentMeta<typeof ChatUserBadge>;
const Template: ComponentStory<typeof ChatUserBadge> = args => <ChatUserBadge {...args} />;
export const Moderator = Template.bind({});
Moderator.args = {
badge: 'mod',
userColor: '5',
};
export const Authenticated = Template.bind({});
Authenticated.args = {
badge: 'auth',
userColor: '6',
};

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatUserMessage from './index';
import { ChatMessage } from '../../../interfaces/chat-message.model';
import Mock from '../../../stories/assets/mocks/chatmessage-user.png';
export default {
title: 'owncast/Chat/Messages/Standard user',
component: ChatUserMessage,
parameters: {
design: {
type: 'image',
url: Mock,
scale: 0.5,
},
docs: {
description: {
component: `This is the standard text message design that is used when a user sends a message in Owncast chat.`,
},
},
},
} as ComponentMeta<typeof ChatUserMessage>;
const Template: ComponentStory<typeof ChatUserMessage> = args => <ChatUserMessage {...args} />;
const standardMessage: ChatMessage = JSON.parse(`{
"type": "CHAT",
"id": "wY-MEXwnR",
"timestamp": "2022-04-28T20:30:27.001762726Z",
"user": {
"id": "h_5GQ6E7R",
"displayName": "EliteMooseTaskForce",
"displayColor": 3,
"createdAt": "2022-03-24T03:52:37.966584694Z",
"previousNames": ["gifted-nobel", "EliteMooseTaskForce"],
"nameChangedAt": "2022-04-26T23:56:05.531287897Z",
"scopes": []
},
"body": "Test message from a regular user."}`);
const moderatorMessage: ChatMessage = JSON.parse(`{
"type": "CHAT",
"id": "wY-MEXwnR",
"timestamp": "2022-04-28T20:30:27.001762726Z",
"user": {
"id": "h_5GQ6E7R",
"displayName": "EliteMooseTaskForce",
"displayColor": 2,
"createdAt": "2022-03-24T03:52:37.966584694Z",
"previousNames": ["gifted-nobel", "EliteMooseTaskForce"],
"nameChangedAt": "2022-04-26T23:56:05.531287897Z",
"scopes": ["moderator"]
},
"body": "I am a moderator user."}`);
const authenticatedUserMessage: ChatMessage = JSON.parse(`{
"type": "CHAT",
"id": "wY-MEXwnR",
"timestamp": "2022-04-28T20:30:27.001762726Z",
"user": {
"id": "h_5GQ6E7R",
"displayName": "EliteMooseTaskForce",
"displayColor": 7,
"createdAt": "2022-03-24T03:52:37.966584694Z",
"previousNames": ["gifted-nobel", "EliteMooseTaskForce"],
"nameChangedAt": "2022-04-26T23:56:05.531287897Z",
"authenticated": true,
"scopes": []
},
"body": "I am an authenticated user."}`);
export const WithoutModeratorMenu = Template.bind({});
WithoutModeratorMenu.args = {
message: standardMessage,
showModeratorMenu: false,
};
export const WithModeratorMenu = Template.bind({});
WithModeratorMenu.args = {
message: standardMessage,
showModeratorMenu: true,
};
export const FromModeratorUser = Template.bind({});
FromModeratorUser.args = {
message: moderatorMessage,
showModeratorMenu: false,
isAuthorModerator: true,
};
export const FromAuthenticatedUser = Template.bind({});
FromAuthenticatedUser.args = {
message: authenticatedUserMessage,
showModeratorMenu: false,
isAuthorAuthenticated: true,
};
export const WithStringHighlighted = Template.bind({});
WithStringHighlighted.args = {
message: standardMessage,
showModeratorMenu: false,
highlightString: 'message',
};