Add more placeholder components and some mocks

This commit is contained in:
Gabe Kangas 2022-05-12 20:52:19 -07:00
parent d427c0ad70
commit ae78716572
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
26 changed files with 305 additions and 18 deletions

View File

@ -14,7 +14,7 @@ module.exports = {
'@storybook/preset-scss',
'@storybook/addon-postcss',
'@storybook/addon-a11y',
'@storybook/addon-viewport',
'storybook-addon-designs',
'storybook-dark-mode',
'addon-screen-reader',
],

View File

@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function NotifyReminderPopup(props: Props) {
return <div>Popup reminder goes here</div>;
}

138
web/package-lock.json generated
View File

@ -38,6 +38,7 @@
"recoil": "^0.7.2",
"slate": "^0.78.0",
"slate-react": "^0.79.0",
"storybook-addon-designs": "^6.2.1",
"ua-parser-js": "1.0.2",
"video.js": "^7.18.1"
},
@ -2224,6 +2225,26 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@figspec/components": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.1.tgz",
"integrity": "sha512-UvnEamPEAMh9HExViqpobWmX25g1+soA9kcJu+It3VerMa7CeVyaIbQydNf1Gys5v/rxJVdTDRgQ7OXW2zAAig==",
"dependencies": {
"lit": "^2.1.3"
}
},
"node_modules/@figspec/react": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.1.tgz",
"integrity": "sha512-4TGazTVFG1SJ+mJNzi3HPKnRC8IGBF6J7GR+wXCPTrOhWpRyQOvcyfomiluPUh69lMLs6hsawjz9yVtutyJocQ==",
"dependencies": {
"@figspec/components": "^1.0.0",
"@lit-labs/react": "^1.0.2"
},
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0"
}
},
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@ -3350,6 +3371,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@lit-labs/react": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.0.4.tgz",
"integrity": "sha512-Sy4RCxTZpTE2TwByjVac6gC7jzr8bCQC6s9bJhhJDKos4ZlN2cqcuVz63n0mpSSr1zxFh+Z0o0VR223l36Y4Ag=="
},
"node_modules/@lit/reactive-element": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.2.tgz",
"integrity": "sha512-A2e18XzPMrIh35nhIdE4uoqRzoIpEU5vZYuQN4S3Ee1zkGdYC27DP12pewbw/RLgPHzaE4kx/YqxMzebOpm0dA=="
},
"node_modules/@mdx-js/loader": {
"version": "1.6.22",
"resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-1.6.22.tgz",
@ -11047,6 +11078,11 @@
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
"integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ=="
},
"node_modules/@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
"node_modules/@types/ua-parser-js": {
"version": "0.7.36",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz",
@ -22767,6 +22803,33 @@
"uc.micro": "^1.0.1"
}
},
"node_modules/lit": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.2.3.tgz",
"integrity": "sha512-5/v+r9dH3Pw/o0rhp/qYk3ERvOUclNF31bWb0FiW6MPgwdQIr+/KCt/p3zcd8aPl8lIGnxdGrVcZA+gWS6oFOQ==",
"dependencies": {
"@lit/reactive-element": "^1.3.0",
"lit-element": "^3.2.0",
"lit-html": "^2.2.0"
}
},
"node_modules/lit-element": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz",
"integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==",
"dependencies": {
"@lit/reactive-element": "^1.3.0",
"lit-html": "^2.2.0"
}
},
"node_modules/lit-html": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.3.tgz",
"integrity": "sha512-vI4j3eWwtQaR8q/O63juZVliBIFMio716X719/lSsGH4UWPy2/7Qf377jsNs4cx3gCHgIbx8yxFgXFQ/igZyXQ==",
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
},
"node_modules/loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@ -29272,6 +29335,14 @@
"resolved": "https://registry.npmjs.org/store2/-/store2-2.13.2.tgz",
"integrity": "sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg=="
},
"node_modules/storybook-addon-designs": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/storybook-addon-designs/-/storybook-addon-designs-6.2.1.tgz",
"integrity": "sha512-ihsscab8185HnxqTNZlM4TfrCPVsO7AimVA8BapuqT/sfZQF9m5H9C0plT3kbECdIMh2cmzMBF1Tc9ckWRgpWg==",
"dependencies": {
"@figspec/react": "^1.0.0"
}
},
"node_modules/storybook-dark-mode": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.9.tgz",
@ -33650,6 +33721,23 @@
"strip-json-comments": "^3.1.1"
}
},
"@figspec/components": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.1.tgz",
"integrity": "sha512-UvnEamPEAMh9HExViqpobWmX25g1+soA9kcJu+It3VerMa7CeVyaIbQydNf1Gys5v/rxJVdTDRgQ7OXW2zAAig==",
"requires": {
"lit": "^2.1.3"
}
},
"@figspec/react": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.1.tgz",
"integrity": "sha512-4TGazTVFG1SJ+mJNzi3HPKnRC8IGBF6J7GR+wXCPTrOhWpRyQOvcyfomiluPUh69lMLs6hsawjz9yVtutyJocQ==",
"requires": {
"@figspec/components": "^1.0.0",
"@lit-labs/react": "^1.0.2"
}
},
"@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@ -34589,6 +34677,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@lit-labs/react": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.0.4.tgz",
"integrity": "sha512-Sy4RCxTZpTE2TwByjVac6gC7jzr8bCQC6s9bJhhJDKos4ZlN2cqcuVz63n0mpSSr1zxFh+Z0o0VR223l36Y4Ag=="
},
"@lit/reactive-element": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.2.tgz",
"integrity": "sha512-A2e18XzPMrIh35nhIdE4uoqRzoIpEU5vZYuQN4S3Ee1zkGdYC27DP12pewbw/RLgPHzaE4kx/YqxMzebOpm0dA=="
},
"@mdx-js/loader": {
"version": "1.6.22",
"resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-1.6.22.tgz",
@ -40388,6 +40486,11 @@
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
"integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ=="
},
"@types/trusted-types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
"integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg=="
},
"@types/ua-parser-js": {
"version": "0.7.36",
"resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz",
@ -49564,6 +49667,33 @@
"uc.micro": "^1.0.1"
}
},
"lit": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/lit/-/lit-2.2.3.tgz",
"integrity": "sha512-5/v+r9dH3Pw/o0rhp/qYk3ERvOUclNF31bWb0FiW6MPgwdQIr+/KCt/p3zcd8aPl8lIGnxdGrVcZA+gWS6oFOQ==",
"requires": {
"@lit/reactive-element": "^1.3.0",
"lit-element": "^3.2.0",
"lit-html": "^2.2.0"
}
},
"lit-element": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz",
"integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==",
"requires": {
"@lit/reactive-element": "^1.3.0",
"lit-html": "^2.2.0"
}
},
"lit-html": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.3.tgz",
"integrity": "sha512-vI4j3eWwtQaR8q/O63juZVliBIFMio716X719/lSsGH4UWPy2/7Qf377jsNs4cx3gCHgIbx8yxFgXFQ/igZyXQ==",
"requires": {
"@types/trusted-types": "^2.0.2"
}
},
"loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@ -54467,6 +54597,14 @@
"resolved": "https://registry.npmjs.org/store2/-/store2-2.13.2.tgz",
"integrity": "sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg=="
},
"storybook-addon-designs": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/storybook-addon-designs/-/storybook-addon-designs-6.2.1.tgz",
"integrity": "sha512-ihsscab8185HnxqTNZlM4TfrCPVsO7AimVA8BapuqT/sfZQF9m5H9C0plT3kbECdIMh2cmzMBF1Tc9ckWRgpWg==",
"requires": {
"@figspec/react": "^1.0.0"
}
},
"storybook-dark-mode": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.9.tgz",

View File

@ -42,6 +42,7 @@
"recoil": "^0.7.2",
"slate": "^0.78.0",
"slate-react": "^0.79.0",
"storybook-addon-designs": "^6.2.1",
"ua-parser-js": "1.0.2",
"video.js": "^7.18.1"
},

View File

@ -5,7 +5,13 @@ import ActionButton from '../components/action-buttons/ActionButton';
export default {
title: 'owncast/Action Buttons/Single button',
component: ActionButton,
parameters: {},
parameters: {
docs: {
description: {
component: `An **Action Button** or **External Action Button** is a button that is used to trigger either an internal or external action. Many will show a modal, but they can also open a new tab to allow navigating to external pages. They are rendered horizontally within the Action Button Row.`,
},
},
},
} as ComponentMeta<typeof ActionButton>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -4,9 +4,16 @@ import ActionButtonRow from '../components/action-buttons/ActionButtonRow';
import ActionButton from '../components/action-buttons/ActionButton';
export default {
title: 'owncast/Action Buttons/Row',
title: 'owncast/Action Buttons/Buttons Row',
component: ActionButtonRow,
parameters: {},
parameters: {
docs: {
description: {
component: `This is a horizontal row of buttons that could be statically created by the Owncast application (such as Notify, Follow) or are user-generated external actions (Donate, Learn more, etc).
There can be any number of buttons, including zero. They should wrap if needed and handle resizing.`,
},
},
},
} as ComponentMeta<typeof ActionButtonRow>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,6 +1,7 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import BrowserNotifyModal from '../components/modals/BrowserNotifyModal';
import BrowserNotifyModalMock from './assets/mocks/notify-modal.png';
const Example = () => (
<div>
@ -11,7 +12,23 @@ const Example = () => (
export default {
title: 'owncast/Modals/Browser Notifications',
component: BrowserNotifyModal,
parameters: {},
parameters: {
design: {
type: 'image',
url: BrowserNotifyModalMock,
scale: 0.5,
},
docs: {
description: {
component: `The notify modal allows an end user to get notified when the stream goes live via [Browser Push Notifications](https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications) It must:
- Verify the browser supports notifications.
- Handle errors that come back from the server.
- Have an enabled and disabled state with accurate information about each.
`,
},
},
},
} as ComponentMeta<typeof BrowserNotifyModal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,11 +1,22 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatActionMessage from '../components/chat/ChatActionMessage';
import Mock from './assets/mocks/chatmessage-action.png';
export default {
title: 'owncast/Chat/Messages/Chat action',
component: ChatActionMessage,
parameters: {},
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>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,11 +1,22 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import ChatSystemMessage from '../components/chat/ChatSystemMessage';
import Mock from './assets/mocks/chatmessage-system.png';
export default {
title: 'owncast/Chat/Messages/System',
component: ChatSystemMessage,
parameters: {},
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} />;

View File

@ -2,11 +2,16 @@ import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import ChatTextField from '../components/chat/ChatTextField/ChatTextField';
import Mock from './assets/mocks/chatinput-mock.png';
export default {
title: 'owncast/Chat/Input text field',
component: ChatTextField,
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `

View File

@ -2,11 +2,23 @@ import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import UserChatMessage from '../components/chat/ChatUserMessage';
import { ChatMessage } from '../interfaces/chat-message.model';
import Mock from './assets/mocks/chatmessage-user.png';
export default {
title: 'owncast/Chat/Messages/Standard user',
component: UserChatMessage,
parameters: {},
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 UserChatMessage>;
const Template: ComponentStory<typeof UserChatMessage> = args => <UserChatMessage {...args} />;

View File

@ -1,6 +1,7 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import FediAuthModal from '../components/modals/FediAuthModal';
import FediAuthModalMock from './assets/mocks/fediauth-modal.png';
const Example = () => (
<div>
@ -11,7 +12,13 @@ const Example = () => (
export default {
title: 'owncast/Modals/FediAuth',
component: FediAuthModal,
parameters: {},
parameters: {
design: {
type: 'image',
url: FediAuthModalMock,
scale: 0.5,
},
},
} as ComponentMeta<typeof FediAuthModal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,6 +1,7 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import FollowModal from '../components/modals/FollowModal';
import FollowModalMock from './assets/mocks/follow-modal.png';
const Example = () => (
<div>
@ -11,7 +12,23 @@ const Example = () => (
export default {
title: 'owncast/Modals/Follow',
component: FollowModal,
parameters: {},
parameters: {
design: {
type: 'image',
url: FollowModalMock,
scale: 0.5,
},
docs: {
description: {
component: `The Follow modal allows an end user to type in their Fediverse account information to follow this Owncast instance. It must:
- Validate the input to make sure it's a valid looking account.
- Handle errors that come back from the server.
- Perform the redirect to the remote server when the backend response is received.
`,
},
},
},
} as ComponentMeta<typeof FollowModal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -1,6 +1,7 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import IndieAuthModal from '../components/modals/IndieAuthModal';
import Mock from './assets/mocks/indieauth-modal.png';
const Example = () => (
<div>
@ -11,7 +12,13 @@ const Example = () => (
export default {
title: 'owncast/Modals/IndieAuth',
component: IndieAuthModal,
parameters: {},
parameters: {
design: {
type: 'image',
url: Mock,
scale: 0.5,
},
},
} as ComponentMeta<typeof IndieAuthModal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars

View File

@ -16,6 +16,7 @@ Visit the [UIv2 milestone](https://github.com/owncast/owncast/milestone/18) on G
- Address feedback from users.
- Better accessibility.
- Better mobile experience.
- Updated [design](https://www.figma.com/file/B6ICOn1J3dyYeoZM5kPM2A/Owncast---Review?node-id=528%3A18).
- Standardize styling across the project by using a single design language and style guide.
- Allows more people to contribute to the project if we use popular frameworks.
@ -25,6 +26,21 @@ Visit the [UIv2 milestone](https://github.com/owncast/owncast/milestone/18) on G
- [React](https://reactjs.org/)
- [Ant Design](https://ant.design/)
## Contributing
1. Find a component that hasn't yet been worked on by looking through the [UIv2 milestone](https://github.com/owncast/owncast/milestone/18)
and the sidebar of components to the left.
1. See if you can have an example of this functionality in action via the [Owncast Demo Server](https://watch.owncast.online) or [Owncast Nightly Build](https://nightly.owncast.online) so you know how it's supposed to work if it's interactive.
1. Visit the `Docs` tab to read any specific documentation that may have been written about how this component works.
1. Go to the `Canvas` tab of the component you selected and see if there's a Design attached to it.
1. If there is a design, then that's a starting point you can use to start building out the component.
1. If there isn't, then visit the [Owncast Demo Server](https://watch.owncast.online), the [Owncast Nightly Build](https://nightly.owncast.online), or the proposed [v2 design](https://www.figma.com/file/B6ICOn1J3dyYeoZM5kPM2A/Owncast---Review?node-id=0%3A1) for some ways to start.
1. If no design exists, then you can ask around the Owncast chat for help, for come up with your own ideas!
1. No designs are stuck in stone, and we're using this as an opportunity to level up the UI of Owncast, so all ideas are welcome.
## How?
This rewrite is a large project, but like anything else, breaking it into pieces and working on one thing at a time will eventually get us to the finish line.
@ -45,9 +61,3 @@ For example a button may have a disabled state that requires a specific scenario
but here we you can just toggle the state to verify things are working as expected.
This means [new components should have a corresponding story added](https://storybook.js.org/docs/react/get-started/whats-a-story) to make it easier to maintain the project.
## Contributing
This is a standard React project so it makes it easy for **anybody to help!**

View File

@ -5,7 +5,13 @@ import Modal from '../components/ui/Modal/Modal';
export default {
title: 'owncast/Modals/Container',
component: Modal,
parameters: {},
parameters: {
docs: {
description: {
component: `This is the popup modal container that all modal content is rendered inside. It can be passed content nodes to render, or a URL to show an iframe.`,
},
},
},
} as ComponentMeta<typeof Modal>;
const Template: ComponentStory<typeof Modal> = args => {

View File

@ -0,0 +1,26 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import NotifyReminder from '../components/ui/NotifyReminderPopup/NotifyReminderPopup';
import Mock from './assets/mocks/notify-popup.png';
export default {
title: 'owncast/Notify Reminder',
component: NotifyReminder,
parameters: {
design: {
type: 'image',
url: Mock,
},
docs: {
description: {
component: `After visiting the page three times this popup reminding you that you can register for live stream notifications shows up.
Clicking it will make the notificaiton modal display. Clicking the "X" will hide the modal and make it never show again.`,
},
},
},
} as ComponentMeta<typeof NotifyReminder>;
const Template: ComponentStory<typeof NotifyReminder> = args => <NotifyReminder {...args} />;
export const Example = Template.bind({});
Example.args = {};

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB