-
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 17 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,38 @@ | ||
| import * as React from 'react'; | ||
|
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. move this page under experiments |
||
| import { ChicletPage } from 'office-ui-fabric-react/lib/components/Chiclet/ChicletPage'; | ||
| import { PageHeader } from '../../components/PageHeader/PageHeader'; | ||
| import { ComponentPage } from '../../components/ComponentPage/ComponentPage'; | ||
| const pageStyles: any = require('../PageStyles.module.scss'); | ||
|
|
||
| export class ChicletComponentPage extends React.Component<any, any> { | ||
| public render() { | ||
| return ( | ||
| <div ref='pageElement' className={ pageStyles.basePage }> | ||
| <ComponentPage> | ||
| <PageHeader pageTitle='Chiclet' backgroundColor='#038387' | ||
| links={ | ||
| [ | ||
| { | ||
| 'text': 'Overview', | ||
| 'location': 'Overview' | ||
| }, | ||
| { | ||
| 'text': 'Best Practices', | ||
| 'location': 'BestPractices' | ||
| }, | ||
| { | ||
| 'text': 'Variants', | ||
| 'location': 'Variants' | ||
| }, | ||
| { | ||
| 'text': 'Implementation', | ||
| 'location': 'Implementation' | ||
| } | ||
| ] | ||
| } /> | ||
| <ChicletPage isHeaderVisible={ false } /> | ||
| </ComponentPage> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,5 +29,11 @@ | |
| "license": "MIT", | ||
| "devDependencies": { | ||
| "@microsoft/rush": "4.3.0" | ||
| }, | ||
| "dependencies": { | ||
|
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. We don;t need these anymore, right? |
||
| "@types/cheerio": "^0.22.7", | ||
| "@types/htmlparser2": "^3.7.31", | ||
| "htmlparser": "^1.7.7", | ||
| "htmlparser2": "^3.9.2" | ||
| } | ||
| } | ||
| 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,73 @@ | ||
| import * as React from 'react'; | ||
| import { | ||
| BaseComponent, | ||
| css | ||
| } from '../../Utilities'; | ||
| import { ChicletCard } from './ChicletCard'; | ||
| import { Chiclet } from './Chiclet'; | ||
| import { IBaseChicletProps, IChicletCardProps } from './Chiclet.types'; | ||
| import { IconButton, IButtonProps } from 'office-ui-fabric-react/lib/Button'; | ||
|
|
||
| export class BaseChiclet extends BaseComponent<IBaseChicletProps, any> { | ||
| public render() { | ||
| const { url, size, actions } = this.props; | ||
|
|
||
| let chicletCardProps = this.extractMetaTags(url); | ||
|
Member
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. I'm curious to see what others think here being relatively new to React. Do we want to be fetching these attributes on render? And if so is
Member
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. If we need to fetch, perhaps
Contributor
Author
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. Good point. I'm also interested to hear what other people think!
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. +1.Let's add it to componentWillReceiveProps and put the results in state, so we get to recompute if and only if the input url changes |
||
|
|
||
| return ( | ||
| <Chiclet chicletCardProps={ chicletCardProps } size={ size ? size : "medium" } actions={ actions } /> | ||
|
Member
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. As mentioned elsewhere can prob use Enum value here for type safety |
||
| ); | ||
| } | ||
|
|
||
| public extractMetaTags(url: string) { | ||
|
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. I'd put this one and the next method in a separate OpenGraph.ts file as static methods. |
||
| var attributes: IChicletCardProps = {}; | ||
|
|
||
| var xmlHttp = new XMLHttpRequest(); | ||
| xmlHttp.open("GET", url, false); | ||
| xmlHttp.overrideMimeType('text/xml'); | ||
| xmlHttp.send(null); | ||
|
|
||
| var metaElements = document.getElementsByTagName("meta"); | ||
| let openGraphObject = this._getOpenGraphValues(metaElements, attributes); | ||
|
|
||
| return openGraphObject; | ||
| } | ||
|
|
||
| public _getOpenGraphValues(metaElements: NodeListOf<HTMLMetaElement>, attributes: IChicletCardProps): IChicletCardProps { | ||
| for (var i = 0; i < metaElements.length; i++) { | ||
| if (metaElements[i].attributes != null && metaElements[i].attributes.length >= 2) { | ||
| switch (metaElements[i].attributes[0].nodeValue) { | ||
| case "og:title": | ||
| attributes.title = metaElements[i].content; | ||
| break; | ||
| case "og:type": | ||
| attributes.ogType = metaElements[i].content; | ||
| break; | ||
| case "og:image": | ||
| case "og:image:url": | ||
| attributes.image = metaElements[i].content; | ||
| break; | ||
| case "og:image:secure_url": | ||
| attributes.imageSecureUrl = metaElements[i].content; | ||
| break; | ||
| case "og:image:type": | ||
| attributes.imageType = metaElements[i].content; | ||
| break; | ||
| case "og:image:width": | ||
| attributes.imageWidth = metaElements[i].content; | ||
| break; | ||
| case "og:image:height": | ||
| attributes.imageHeight = metaElements[i].content; | ||
| break; | ||
| case "og:description": | ||
| attributes.description = metaElements[i].content; | ||
| break; | ||
| case "og:url": | ||
| attributes.url = metaElements[i].content; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return attributes; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| import { memoizeFunction } from '../../Utilities'; | ||
| import { | ||
| mergeStyleSets, | ||
| IStyle, | ||
| ITheme, | ||
| concatStyleSets, | ||
| getTheme | ||
| } from '../../Styling'; | ||
| import { IChicletStyles } from './Chiclet.types'; | ||
|
|
||
| const ChicletCardTitleLineHeight = '21px'; | ||
|
|
||
| /* Actions */ | ||
| const msChicletCardActionsActionSize = '34px'; | ||
| const msChicletCardActionsHorizontalPadding = '12px'; | ||
| const msChicletCardActionsVerticalPadding = '2px'; | ||
|
|
||
| export const getClassNames = memoizeFunction(( | ||
|
Member
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. Please avoid using Follow the guidance here: |
||
| theme: ITheme = getTheme(), | ||
| customStyles?: IChicletStyles | ||
| ): IChicletStyles => { | ||
| const styles: IChicletStyles = { | ||
| root: { | ||
| WebkitFontSmoothing: 'antialiased', | ||
| backgroundColor: 'white', | ||
| border: `1px solid ${theme.palette.neutralLight}`, | ||
| boxSizing: 'border-box', | ||
| maxWidth: '320px', | ||
| minWidth: '540px', | ||
| height: '109px', | ||
| userSelect: 'none', | ||
| position: 'relative', | ||
| selectors: { | ||
| ':hover': { | ||
| cursor: 'pointer', | ||
| border: `1px solid ${theme.palette.neutralTertiaryAlt}` | ||
| } | ||
| } | ||
| }, | ||
| icon: [ | ||
| 'ms-DocumentCardPreview-icon', | ||
| { | ||
| marginLeft: '10px', | ||
| bottom: '10px', | ||
| position: 'absolute', | ||
| color: '#166EBE' | ||
| } | ||
| ], | ||
| preview: [ | ||
| 'ms-ChicletCardPreview', | ||
| { | ||
| float: 'left', | ||
| maxHeight: '107px', | ||
| maxWidth: '160px', | ||
| position: 'relative', | ||
| overflow: 'hidden', // need to fix | ||
| backgroundColor: theme.palette.neutralLighterAlt, | ||
| display: 'block' | ||
| } | ||
| ], | ||
| info: [ | ||
| 'ms-ChicletCardInfo', | ||
| { | ||
| position: 'relative', | ||
| display: 'block', | ||
| height: '100%', | ||
| lineHeight: '21px', | ||
| overflow: 'hidden', | ||
| wordWrap: 'break-word' | ||
| } | ||
| ], | ||
| title: [ | ||
| 'ms-ChicletCardTitle', | ||
| { | ||
| padding: '8px 16px 0px', | ||
| font: theme.fonts.large, | ||
| color: theme.palette.neutralPrimary, | ||
| height: '42px', // Two lines of text, making sure the third line is hidden | ||
| lineHeight: ChicletCardTitleLineHeight, | ||
| overflow: 'hidden', | ||
| wordWrap: 'break-word', | ||
| fontWeight: '400' | ||
| } | ||
| ], | ||
| link: [ | ||
| 'ms-ChicletCardLink', | ||
| { | ||
| padding: '4px 16px 0px', | ||
| fontSize: '8pt', | ||
| color: theme.palette.neutralTertiaryAlt, | ||
| lineHeight: '14px', | ||
| height: '14px', | ||
| overflow: 'hidden', | ||
| whiteSpace: 'nowrap', | ||
| textOverflow: 'ellipsis' | ||
| } | ||
| ], | ||
| actions: [ | ||
| 'ms-ChicletFooter', | ||
| { | ||
| height: msChicletCardActionsActionSize, | ||
| padding: `${msChicletCardActionsVerticalPadding} ${msChicletCardActionsHorizontalPadding}`, | ||
| position: 'relative' | ||
| } | ||
| ], | ||
| action: [ | ||
| 'ms-ChicletFooter-action', | ||
| { | ||
| float: 'right', | ||
| marginLeft: '4px', | ||
| cursor: 'pointer' | ||
| } | ||
| ] | ||
| }; | ||
|
|
||
| return concatStyleSets(styles, customStyles)!; | ||
|
|
||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import * as React from 'react'; | ||
| import { | ||
| css | ||
| } from '../../Utilities'; | ||
| import { | ||
| getClassNames | ||
| } from './Chiclet.styles'; | ||
| import { IChicletProps, IChicletCardProps, IChicletStyles } from './Chiclet.types'; | ||
| import { mergeStyles } from '../../Styling'; | ||
| import { ChicletCard } from './ChicletCard'; | ||
| import { IButtonProps } from 'office-ui-fabric-react/lib/Button'; | ||
|
|
||
| export class Chiclet extends React.Component<IChicletProps, IChicletCardProps> { | ||
| private _classNames: IChicletStyles = {}; | ||
|
|
||
| public render() { | ||
| const { styles: customStyles, chicletCardProps, size, actions, theme } = this.props; | ||
|
Member
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. Can probably add |
||
| this._classNames = getClassNames(theme!, customStyles); | ||
|
|
||
| var actionsToIButtonProps: IButtonProps[] = []; | ||
|
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. I suggest let's add the actions when we get there, or put some more thought to this. Each action will possibly need to contain more information, like at least an onClick handler. We could have something like: interface IChicletAction { |
||
| if (actions != null) { | ||
| actions.forEach(function (string) { | ||
| switch (string) { | ||
| case "Breadcrumb": | ||
| actionsToIButtonProps.push({ iconProps: { iconName: 'Breadcrumb' } }); | ||
| break; | ||
| case "Save": | ||
| actionsToIButtonProps.push({ iconProps: { iconName: 'Save' } }); | ||
| break; | ||
| case "Share": | ||
| actionsToIButtonProps.push({ iconProps: { iconName: 'Share' } }); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| switch (size) { | ||
|
Member
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. Each case returns the same DOM as far as I can tell. Intentional as an area of extensibility later?
Contributor
Author
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. Yes exactly!
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. In general, we should not add extra prototype code that we know will need to change eventually. Let's just add to this when we get there. Maybe add a @todo comment to mention what you intend to do. |
||
| case "xsmall": | ||
| return ( | ||
| <ChicletCard {...chicletCardProps} className={ mergeStyles(this._classNames.root) } onClick={ this._onClick } actions={ actionsToIButtonProps } /> | ||
| ); | ||
| case "small": | ||
| return ( | ||
| <ChicletCard {...chicletCardProps} className={ mergeStyles(this._classNames.root) } onClick={ this._onClick } actions={ actionsToIButtonProps } /> | ||
| ); | ||
| case "medium": | ||
| case "large": | ||
| case "xlarge": | ||
| return ( | ||
| <ChicletCard {...chicletCardProps} className={ mergeStyles(this._classNames.root) } onClick={ this._onClick } actions={ actionsToIButtonProps } /> | ||
| ); | ||
| default: | ||
| return ( | ||
| <ChicletCard {...chicletCardProps} className={ mergeStyles(this._classNames.root) } onClick={ this._onClick } actions={ actionsToIButtonProps } /> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| private _onClick(): void { | ||
| console.log("You clicked the Chiclet"); | ||
|
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. // @todo: default click handler |
||
| } | ||
| } | ||
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.
We should add the demo page to office-ui-fabric-react/packages/experiments/src/demo/AppDefinition.tsx instead, as the Chiclet will be in experiments.
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.
you can follow the example of TilesListPage of any other existing component in experiments