-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Experimental Chiclet Component #4678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fb26b91
ad94997
542fcf8
c6f0aab
5d7f668
56f52c9
15ccf73
1012e4c
ee82f29
fd42f49
fc90f50
3fa9d9e
3df9860
a2b0fa5
2e5978d
e497323
d0b31a2
d3a9f0d
acbcbc2
26cb7d0
bbacc49
600e4c8
7742c47
2f3ca5b
4e44852
64af2e7
5b4d21d
4e87878
b9581b7
3719108
7d1104d
f873b97
5020b32
eb3b959
a44db3b
4ba3924
65fba98
2b65727
3965f9a
c91dfe3
806aff1
cbe9c0f
eba2185
00c9ca3
6d762d2
7a35619
db18be7
02849e3
ada5ffe
9a1b9c6
4756ed0
a0d6e73
68b9102
1abe186
d353ed7
6bad6f0
49c756b
972d37d
16156a8
275bbc2
db80bf2
70badd3
5b381fb
8f17013
9a7d9e8
be9118f
b33c5b8
51f1ba8
679e0a6
c404200
7a550dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "@uifabric/experiments", | ||
| "comment": "Implementation of the Chiclet component", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "@uifabric/experiments", | ||
| "email": "naethell@microsoft.com" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "office-ui-fabric-react", | ||
| "comment": "Implementation of experimental chiclet component", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "office-ui-fabric-react", | ||
| "email": "naethell@microsoft.com" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from './components/Chiclet/index'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| const baseProductionCdnUrl = 'https://az742526.vo.msecnd.net/files/odsp-next-release-odc_2018-04-13_20180418.001/odsp-media/images/apps/'; | ||
|
|
||
| export const ChicletTestImages = { | ||
| iconWordDoc: baseProductionCdnUrl + 'word_16x1.svg', | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import * as React from 'react'; | ||
| import { | ||
| BaseComponent, | ||
| customizable | ||
| } from '../../Utilities'; | ||
| import { ChicletCard } from './ChicletCard'; | ||
| import { getOpenGraphProperties } from './OpenGraph'; | ||
| import { IChicletProps, ChicletSize } from './Chiclet.types'; | ||
| import { IChicletCardProps } from './ChicletCard.types'; | ||
|
|
||
| export interface IChicletState { | ||
| chicletCardProps?: IChicletCardProps; | ||
| } | ||
|
|
||
| @customizable('ChicletBase', ['theme']) | ||
| export class ChicletBase extends BaseComponent<IChicletProps, IChicletState> { | ||
|
|
||
| constructor(props: IChicletProps) { | ||
| super(props); | ||
|
|
||
| const chicletCardProps = getOpenGraphProperties(this.props.url); | ||
| this.state = { chicletCardProps: chicletCardProps }; | ||
| } | ||
|
|
||
| public render(): JSX.Element { | ||
| const { size, footer, description } = this.props; | ||
| const { chicletCardProps } = this.state; | ||
|
|
||
| switch (size) { | ||
| case ChicletSize.medium: | ||
| return ( | ||
| <ChicletCard { ...chicletCardProps } onClick={ this._onClick } footer={ footer } description={ description } /> | ||
| ); | ||
| // @todo: handle other types of chiclets | ||
| default: | ||
| return ( | ||
| <ChicletCard { ...chicletCardProps } onClick={ this._onClick } footer={ footer } description={ description } /> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| public componentWillReceiveProps(nextProps: IChicletProps): void { | ||
| if (this.props.url !== nextProps.url) { | ||
| this.setState({ chicletCardProps: getOpenGraphProperties(this.props.url) }); | ||
| } | ||
| } | ||
|
|
||
| private _onClick(): void { // @todo: default click handler | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { IChicletStyleProps, IChicletStyles } from './Chiclet.types'; | ||
|
|
||
| export const getStyles = ( | ||
| props: IChicletStyleProps | ||
| ): IChicletStyles => { | ||
| return ({ | ||
| root: {} | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { styled } from '../../Utilities'; | ||
| import { | ||
| IChicletProps, | ||
| IChicletStyleProps, | ||
| IChicletStyles | ||
| } from './Chiclet.types'; | ||
| import { getStyles } from './Chiclet.styles'; | ||
| import { ChicletBase } from './Chiclet.base'; | ||
|
|
||
| export const Chiclet = styled<IChicletProps, IChicletStyleProps, IChicletStyles>( | ||
| ChicletBase, | ||
| getStyles | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import * as React from 'react'; | ||
| import { ChicletBase } from './Chiclet.base'; | ||
| import { IStyleFunction } from '../../Utilities'; | ||
| import { | ||
| IStyle, | ||
| ITheme | ||
| } from '../../Styling'; | ||
|
|
||
| export interface IChiclet { | ||
|
|
||
| } | ||
|
|
||
| export interface IChicletProps extends React.Props<ChicletBase> { | ||
| /** | ||
| * Optional callback to access the IChiclet interface. Use this instead of ref for accessing | ||
| * the public methods and properties of the component. | ||
| */ | ||
| componentRef?: (component: IChiclet | null) => void; | ||
|
|
||
| /** | ||
| * Call to provide customized styling that will layer on top of the variant rules. | ||
| */ | ||
| getStyles?: IStyleFunction<IChicletStyleProps, IChicletStyles>; | ||
|
|
||
| /** | ||
| * Optional class for chiclet. | ||
| */ | ||
| className?: string; | ||
|
|
||
| /** | ||
| * Sharing link | ||
| */ | ||
| url: string; | ||
|
|
||
| /** | ||
| * Chiclet size to render | ||
| */ | ||
| size?: ChicletSize; | ||
|
|
||
| /** | ||
| * Description to render for the component. | ||
| */ | ||
| description?: React.ReactElement<JSX.Element>; | ||
|
|
||
| /** | ||
| * Footer to render for the component. | ||
| */ | ||
| footer?: React.ReactElement<JSX.Element>; | ||
|
|
||
| /** | ||
| * Theme for the component. | ||
| */ | ||
| theme?: ITheme; | ||
| } | ||
|
|
||
| export interface IChicletStyleProps { | ||
| /** | ||
| * Theme for the component. | ||
| */ | ||
| theme?: ITheme; | ||
| } | ||
|
|
||
| export interface IChicletStyles { | ||
| /** | ||
| * Style for the root element when fixed. | ||
| */ | ||
| root?: IStyle; | ||
| } | ||
|
|
||
| export enum ChicletSize { | ||
| /** | ||
| * X-Small Chiclet | ||
| */ | ||
| xSmall = 0, | ||
|
|
||
| /** | ||
| * Small Chiclet | ||
| */ | ||
| small = 1, | ||
|
|
||
| /** | ||
| * Medium Chiclet | ||
| */ | ||
| medium = 2, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The naming convention for enum members is InitialCaps
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most enums in the fabric codebase seem to be lowerCamelCased... let's go with consistency now, and if we want to change the convention, do it all in one go? |
||
|
|
||
| /** | ||
| * Large Chiclet | ||
| */ | ||
| large = 3 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| import * as React from 'react'; | ||
| import { | ||
| BaseComponent, | ||
| css, | ||
| customizable, | ||
| classNamesFunction | ||
| } from '../../Utilities'; | ||
| import { IChicletCardStyles, IChicletCardStyleProps, IChicletCardProps } from './ChicletCard.types'; | ||
| import { mergeStyles } from '../../Styling'; | ||
| import { Image } from 'office-ui-fabric-react/lib/Image'; | ||
|
|
||
| const getClassNames = classNamesFunction<IChicletCardStyleProps, IChicletCardStyles>(); | ||
|
|
||
| const ASSET_CDN_BASE_URL = 'https://static2.sharepointonline.com/files/fabric/assets'; | ||
|
|
||
| const PREVIEW_IMAGE_WIDTH = '198px'; | ||
| const PREVIEW_IMAGE_HEIGHT = '122px'; | ||
|
|
||
| @customizable('ChicletCardBase', ['theme']) | ||
| export class ChicletCardBase extends BaseComponent<IChicletCardProps, {}> { | ||
| public static defaultProps: IChicletCardProps = { | ||
| imageWidth: PREVIEW_IMAGE_WIDTH, | ||
| imageHeight: PREVIEW_IMAGE_HEIGHT | ||
| }; | ||
|
|
||
| private _classNames: { [key in keyof IChicletCardStyles]: string }; | ||
|
|
||
| public render(): JSX.Element { | ||
| const { | ||
| title, | ||
| itemType, | ||
| description, | ||
| image, | ||
| imageWidth, | ||
| imageHeight, | ||
| imageAlt, | ||
| url, | ||
| onClick, | ||
| className, | ||
| footer, | ||
| theme, | ||
| getStyles | ||
| } = this.props; | ||
| const actionable = (onClick) ? true : false; | ||
|
|
||
| this._classNames = getClassNames(getStyles, { theme: theme! }); | ||
|
|
||
| // if this element is actionable it should have an aria role | ||
| const role = actionable ? (onClick ? 'button' : 'link') : undefined; | ||
| const tabIndex = actionable ? 0 : undefined; | ||
|
|
||
| const preview = this._renderPreviewImage(image, imageHeight, imageWidth, itemType, imageAlt); | ||
|
|
||
| return ( | ||
| <div | ||
| tabIndex={ tabIndex } | ||
| role={ role } | ||
| onClick={ actionable ? this._onClick : undefined } | ||
| className={ | ||
| css('ms-ChicletCard', className, mergeStyles(this._classNames.root)) } | ||
| > | ||
| <div | ||
| className={ mergeStyles(this._classNames.preview) } | ||
| > | ||
| { preview } | ||
| </div> | ||
| <div | ||
| className={ mergeStyles(this._classNames.info) } | ||
| > | ||
| <div | ||
| className={ mergeStyles(this._classNames.title) } | ||
| > | ||
| { title ? title : (null) } | ||
| </div> | ||
| <div | ||
| className={ mergeStyles(this._classNames.description) } | ||
| > | ||
| { description ? description : url } | ||
| </div> | ||
| { footer } | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| private _renderPreviewImage(imageUrl?: string, imageHeight?: string, imageWidth?: string, itemType?: string, imageAlt?: string) | ||
| : React.ReactElement<React.HTMLAttributes<HTMLDivElement>> { | ||
| let image; | ||
| if (imageUrl) { | ||
| image = ( | ||
| <Image | ||
| width={ imageWidth } | ||
| height={ imageHeight } | ||
| src={ imageUrl } | ||
| role='presentation' | ||
| alt={ imageAlt ? imageAlt : undefined } | ||
| /> | ||
| ); | ||
| } else { | ||
| image = ( | ||
| <Image | ||
| width={ PREVIEW_IMAGE_WIDTH } | ||
| height={ PREVIEW_IMAGE_HEIGHT } | ||
| src={ itemType | ||
| ? `${ASSET_CDN_BASE_URL}/brand-icons/document/svg/` + itemType + `_48x1.svg` | ||
| : (undefined) /* @todo: this will be replaced by something built by the design team */ } | ||
| role='presentation' | ||
| alt={ imageAlt ? imageAlt : undefined } | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| let src; | ||
| if (itemType !== null) { | ||
| src = `${ASSET_CDN_BASE_URL}/brand-icons/product/svg/` + itemType + `_16x1_5.svg`; | ||
| } | ||
| let icon = <img className={ mergeStyles(this._classNames.icon) } src={ src } />; | ||
| switch (itemType) { // for "hero" apps, we'll use the app icons | ||
| case 'word': | ||
| case 'docx': | ||
| icon = ( | ||
| <img | ||
| className={ mergeStyles(this._classNames.icon) } | ||
| src={ `${ASSET_CDN_BASE_URL}/brand-icons/product/svg/word_16x1_5.svg` } | ||
| /> | ||
| ); | ||
| break; | ||
| case 'powerpoint': | ||
| case 'pptx': | ||
| icon = ( | ||
| <img | ||
| className={ mergeStyles(this._classNames.icon) } | ||
| src={ `${ASSET_CDN_BASE_URL}/brand-icons/product/svg/powerpoint_16x1_5.svg` } | ||
| /> | ||
| ); | ||
| break; | ||
| case 'excel': | ||
| icon = ( | ||
| <img | ||
| className={ mergeStyles(this._classNames.icon) } | ||
| src={ `${ASSET_CDN_BASE_URL}/brand-icons/product/svg/excel_16x1_5.svg` } | ||
| /> | ||
| ); | ||
| break; | ||
| } | ||
|
|
||
| return ( | ||
| <div> | ||
| { image } | ||
| { icon } | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| private _onClick = (ev: React.MouseEvent<HTMLElement>): void => { | ||
| const { onClick } = this.props; | ||
| if (onClick) { | ||
| onClick(ev); | ||
| } | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's split this file into 3 one for each component: ChicletCard.types.ts, Chiclet.types.ts and BaseChiclet.types.ts