generated from gravity-ui/package-example
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Stories): add unstable versions of Stories and StoriesGroup comp…
…onents
- Loading branch information
1 parent
5f3a336
commit 0685024
Showing
32 changed files
with
1,439 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
## Stories | ||
|
||
Component for displaying stories. It looks like a carousel in a modal with given places to display text and media. | ||
|
||
### PropTypes | ||
|
||
| Property | Type | Required | Default | Description | | ||
| :------------------ | :-------------- | :------- | :------ | :----------------------------------------------- | | ||
| open | `Boolean` | ✓ | | Visibility flag | | ||
| items | `StoriesItem[]` | ✓ | | List of stories to display | | ||
| initialStoryIndex | `Number` | | 0 | Index of the first story to be displayed | | ||
| onClose | `Function` | | | Action on close | | ||
| onPreviousClick | `Function` | | | Action when switching to previous story | | ||
| onNextClick | `Function` | | | Action when switching to next story | | ||
| disableOutsideClick | `Boolean` | | true | If `true`, do not close stories on click outside | | ||
| className | `string` | | | Stories modal class | | ||
| action | `ButtonProps` | | | Custom action button props for the last step | | ||
|
||
### StoriesItem object | ||
|
||
| Field | Type | Required | Default | Description | | ||
| ----------- | ------------------ | -------- | ------- | -------------------------------- | | ||
| title | `String` | | | Title | | ||
| description | `String` | | | Main text, deprecated | | ||
| content | `React.ReactNode` | | | Main content | | ||
| url | `String` | | | Link to display more information | | ||
| media | `StoriesItemMedia` | | | Media content | | ||
|
||
### StoriesItemMedia object | ||
|
||
| Field | Type | Required | Default | Description | | ||
| --------- | -------- | -------- | ------- | --------------------------------- | | ||
| type | `String` | | image | Content type (`image` or `video`) | | ||
| url | `String` | ✓ | | File link | | ||
| posterUrl | `String` | | | Poster URL (only used for video) | | ||
|
||
#### Usage example | ||
|
||
```jsx harmony | ||
<Stories | ||
open | ||
items={[ | ||
{ | ||
title: 'Story title', | ||
content: <b>Story text</b>, | ||
media: { | ||
url: 'https://storage.yandexcloud.net/uikit-storybook-assets/story-picture-2.png', | ||
}, | ||
}, | ||
]} | ||
/> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
@use '../../variables'; | ||
|
||
$block: '.#{variables.$ns}stories'; | ||
$borderRadius: 20px; | ||
|
||
#{$block} { | ||
--g-modal-border-radius: #{$borderRadius}; | ||
--g-modal-margin: 20px; | ||
|
||
&__modal-content { | ||
border-radius: $borderRadius; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import React from 'react'; | ||
|
||
import type {ModalCloseReason} from '@gravity-ui/uikit'; | ||
import {Modal} from '@gravity-ui/uikit'; | ||
|
||
import {block} from '../../utils/cn'; | ||
|
||
import { | ||
IndexType, | ||
StoriesLayout, | ||
type StoriesLayoutProps, | ||
} from './components/StoriesLayout/StoriesLayout'; | ||
import {useSyncWithLS} from './hooks'; | ||
import type {StoriesItem} from './types'; | ||
|
||
import './Stories.scss'; | ||
|
||
const b = block('stories'); | ||
|
||
export interface StoriesProps { | ||
open: boolean; | ||
items: StoriesItem[]; | ||
onClose?: ( | ||
event: MouseEvent | KeyboardEvent | React.MouseEvent<HTMLElement, MouseEvent>, | ||
reason: ModalCloseReason | 'closeButtonClick', | ||
) => void; | ||
initialStoryIndex?: number; | ||
onPreviousClick?: (storyIndex: number) => void; | ||
onNextClick?: (storyIndex: number) => void; | ||
disableOutsideClick?: boolean; | ||
className?: string; | ||
action?: StoriesLayoutProps['action']; | ||
syncInTabsKey?: string; | ||
} | ||
|
||
export function Stories({ | ||
open, | ||
onClose, | ||
items, | ||
onPreviousClick, | ||
onNextClick, | ||
initialStoryIndex = 0, | ||
disableOutsideClick = true, | ||
className, | ||
action, | ||
syncInTabsKey, | ||
}: StoriesProps) { | ||
const [storyIndex, setStoryIndex] = React.useState(initialStoryIndex); | ||
|
||
const handleClose = React.useCallback<NonNullable<StoriesProps['onClose']>>( | ||
(event, reason) => { | ||
onClose?.(event, reason); | ||
}, | ||
[onClose], | ||
); | ||
|
||
const {callback: closeWithLS} = useSyncWithLS<NonNullable<StoriesProps['onClose']>>({ | ||
callback: (event, reason) => { | ||
onClose?.(event, reason); | ||
}, | ||
uniqueKey: `close-story-${syncInTabsKey}`, | ||
}); | ||
|
||
const handleButtonClose = React.useCallback< | ||
(event: MouseEvent | KeyboardEvent | React.MouseEvent<HTMLElement, MouseEvent>) => void | ||
>( | ||
(event) => { | ||
handleClose(event, 'closeButtonClick'); | ||
if (syncInTabsKey) closeWithLS(event, 'closeButtonClick'); | ||
}, | ||
[handleClose, syncInTabsKey, closeWithLS], | ||
); | ||
|
||
const handleGotoPrevious = React.useCallback(() => { | ||
setStoryIndex((currentStoryIndex) => { | ||
if (currentStoryIndex <= 0) { | ||
return 0; | ||
} | ||
|
||
const newIndex = currentStoryIndex - 1; | ||
onPreviousClick?.(newIndex); | ||
return newIndex; | ||
}); | ||
}, [onPreviousClick]); | ||
|
||
const handleGotoNext = React.useCallback(() => { | ||
setStoryIndex((currentStoryIndex) => { | ||
if (currentStoryIndex >= items.length - 1) { | ||
return items.length - 1; | ||
} | ||
|
||
const newIndex = currentStoryIndex + 1; | ||
onNextClick?.(newIndex); | ||
return newIndex; | ||
}); | ||
}, [items, onNextClick]); | ||
|
||
if (items.length === 0) { | ||
return null; | ||
} | ||
|
||
// case when items has changed and index has ceased to be valid | ||
if (items[storyIndex] === undefined) { | ||
const correctIndex = items[initialStoryIndex] === undefined ? 0 : initialStoryIndex; | ||
setStoryIndex(correctIndex); | ||
|
||
return null; | ||
} | ||
|
||
const indexType = | ||
(items.length === 1 && IndexType.Single) || | ||
(storyIndex === 0 && IndexType.Start) || | ||
(storyIndex === items.length - 1 && IndexType.End) || | ||
IndexType.InProccess; | ||
|
||
return ( | ||
<Modal | ||
open={open} | ||
onClose={handleClose} | ||
disableOutsideClick={disableOutsideClick} | ||
className={b()} | ||
contentClassName={b('modal-content', className)} | ||
> | ||
<StoriesLayout | ||
items={items} | ||
storyIndex={storyIndex} | ||
indexType={indexType} | ||
handleButtonClose={handleButtonClose} | ||
handleGotoNext={handleGotoNext} | ||
handleGotoPrevious={handleGotoPrevious} | ||
action={action} | ||
/> | ||
</Modal> | ||
); | ||
} |
103 changes: 103 additions & 0 deletions
103
src/components/unstable/Stories/__stories__/Stories.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React from 'react'; | ||
|
||
import {Button} from '@gravity-ui/uikit'; | ||
import type {Meta, StoryFn} from '@storybook/react'; | ||
|
||
import type {StoriesProps} from '../Stories'; | ||
import {Stories} from '../Stories'; | ||
import type {StoriesItem} from '../types'; | ||
|
||
export default { | ||
title: 'Components/Stories', | ||
component: Stories, | ||
} as Meta; | ||
|
||
const items: StoriesItem[] = [ | ||
{ | ||
title: 'New navigation', | ||
content: | ||
'At the top of the panel is the service navigation for each service. ' + | ||
'Below are common navigation elements: a component for switching between accounts ' + | ||
'and organizations, settings, help center, search, notifications, favorites.', | ||
url: 'https://yandex.eu', | ||
media: { | ||
url: 'https://storage.yandexcloud.net/uikit-storybook-assets/story-picture-2.png', | ||
}, | ||
}, | ||
{ | ||
title: 'New navigation (2)', | ||
content: 'A little more about the new navigation', | ||
media: { | ||
url: 'https://storage.yandexcloud.net/uikit-storybook-assets/sample_960x400_ocean_with_audio.mp4', | ||
type: 'video', | ||
}, | ||
}, | ||
{ | ||
title: 'New navigation (3)', | ||
content: <b>Switch to the new navigation right now</b>, | ||
media: { | ||
url: 'https://storage.yandexcloud.net/uikit-storybook-assets/story-picture-4.png', | ||
}, | ||
}, | ||
]; | ||
|
||
const DefaultTemplate: StoryFn<StoriesProps> = (props: StoriesProps) => { | ||
const [visible, setVisible] = React.useState(props.open); | ||
|
||
React.useEffect(() => { | ||
setVisible(props.open); | ||
}, [props.open]); | ||
|
||
return ( | ||
<React.Fragment> | ||
<div> | ||
<Button | ||
onClick={() => { | ||
setVisible(true); | ||
}} | ||
> | ||
Open | ||
</Button> | ||
</div> | ||
<Stories | ||
{...props} | ||
open={visible} | ||
onClose={() => { | ||
setVisible(false); | ||
}} | ||
/> | ||
</React.Fragment> | ||
); | ||
}; | ||
export const Default = DefaultTemplate.bind({}); | ||
Default.args = { | ||
open: false, | ||
items, | ||
}; | ||
Default.argTypes = { | ||
onPreviousClick: {action: 'onPreviousClick'}, | ||
onNextClick: {action: 'onNextClick'}, | ||
}; | ||
|
||
export const Single = DefaultTemplate.bind({}); | ||
Single.args = { | ||
open: false, | ||
items: [items[0]], | ||
}; | ||
|
||
export const WithCustomAction = DefaultTemplate.bind({}); | ||
WithCustomAction.args = { | ||
open: false, | ||
items: [items[0]], | ||
action: { | ||
view: 'action', | ||
children: 'View examples', | ||
}, | ||
}; | ||
|
||
export const WithSyncInTabs = DefaultTemplate.bind({}); | ||
WithSyncInTabs.args = { | ||
open: true, | ||
syncInTabsKey: 'test-story', | ||
items: [items[0]], | ||
}; |
10 changes: 10 additions & 0 deletions
10
src/components/unstable/Stories/components/ImageView/ImageView.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
@use '../../../../variables'; | ||
|
||
$block: '.#{variables.$ns}stories-image-view'; | ||
|
||
#{$block} { | ||
width: auto; | ||
max-width: 100%; | ||
max-height: 100%; | ||
margin: auto; | ||
} |
18 changes: 18 additions & 0 deletions
18
src/components/unstable/Stories/components/ImageView/ImageView.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
|
||
import {block} from '../../../../utils/cn'; | ||
import type {StoriesItemMedia} from '../../types'; | ||
|
||
import './ImageView.scss'; | ||
|
||
const b = block('stories-image-view'); | ||
|
||
export interface ImageViewProps { | ||
media: StoriesItemMedia; | ||
} | ||
|
||
export function ImageView({media}: ImageViewProps) { | ||
const type = media.type || 'image'; | ||
|
||
return type === 'image' ? <img className={b()} src={media.url} alt="" /> : null; | ||
} |
16 changes: 16 additions & 0 deletions
16
src/components/unstable/Stories/components/MediaRenderer/MediaRenderer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from 'react'; | ||
|
||
import {ImageView, VideoView} from '../../components'; | ||
import type {StoriesItemMedia} from '../../types'; | ||
|
||
export interface MediaRendererProps { | ||
media: StoriesItemMedia; | ||
} | ||
|
||
export function MediaRenderer({media}: MediaRendererProps) { | ||
return (media.type || 'image') === 'image' ? ( | ||
<ImageView media={media} /> | ||
) : ( | ||
<VideoView media={media} /> | ||
); | ||
} |
Oops, something went wrong.