-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adds Image web component. * Removes unused portions of the doc file (now spec.md). Renames and simplifies the bordered attribute. Removes multiple image from first story. Adjusts style attribute selctor for bordered. * Removes unused CSS. Changes dimension of image used in first story. * Removes borderRadius attribute and related story code and styles. Adds CSS Guidance to spec to convey details about margin and border-radius values. Cleaned up story code. * Records results of yarn change * Updates package with reference to the component. Makes fit and shape optional. Replaces hard coded border radius value with token. * Update change/@fluentui-web-components-a862230b-9028-4c6a-8a43-3be819a6128e.json Co-authored-by: Miroslav Stastny <mistastn@microsoft.com> --------- Co-authored-by: Chris Holt <chhol@microsoft.com> Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>
- Loading branch information
Showing
12 changed files
with
474 additions
and
0 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-web-components-a862230b-9028-4c6a-8a43-3be819a6128e.json
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,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"comment": "feat(image): Add image web component", | ||
"packageName": "@fluentui/web-components", | ||
"email": "harankin@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
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,4 @@ | ||
import { FluentDesignSystem } from '../fluent-design-system.js'; | ||
import { definition } from './image.definition.js'; | ||
|
||
definition.define(FluentDesignSystem.registry); |
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,17 @@ | ||
import { FluentDesignSystem } from '../fluent-design-system.js'; | ||
import { Image } from './image.js'; | ||
import { template } from './image.template.js'; | ||
import { styles } from './image.styles.js'; | ||
|
||
/** | ||
* The Fluent Image Element | ||
* | ||
* @public | ||
* @remarks | ||
* HTML Element: \<fluent-image\> | ||
*/ | ||
export const definition = Image.compose({ | ||
name: `${FluentDesignSystem.prefix}-image`, | ||
template, | ||
styles, | ||
}); |
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,30 @@ | ||
import { ValuesOf } from '@microsoft/fast-foundation'; | ||
|
||
/** | ||
* Image fit | ||
* @public | ||
*/ | ||
export const ImageFit = { | ||
none: 'none', | ||
center: 'center', | ||
contain: 'contain', | ||
cover: 'cover', | ||
default: 'default', | ||
} as const; | ||
/** | ||
* Types for image fit | ||
* @public | ||
*/ | ||
export type ImageFit = ValuesOf<typeof ImageFit>; | ||
|
||
/** | ||
* Image shape | ||
* @public | ||
*/ | ||
export const ImageShape = { | ||
circular: 'circular', | ||
rounded: 'rounded', | ||
square: 'square', | ||
} as const; | ||
|
||
export type ImageShape = ValuesOf<typeof ImageShape>; |
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,62 @@ | ||
# Fluent Image Component | ||
|
||
## Component Description | ||
|
||
Images, like photos and illustrations, help reinforce a message and express your product or app’s style. | ||
|
||
## Design Spec | ||
|
||
[Image Spec in Figma](https://www.figma.com/file/05wt6TAsEmgsCVZfPrpcWx/Image?t=uEvu1KnTefdTZHJC-6) | ||
|
||
## Engineering Spec | ||
|
||
### Inputs | ||
|
||
**content** | ||
|
||
- @attr public alt: string | Requires description if image role is not set to presentation. | ||
- @attr public role: string | ||
- @attr public src: string | ||
|
||
**booleans** | ||
|
||
- @attr public block: boolean | false | ||
- @attr public border: boolean | false | ||
- @attr public shadow: boolean | false | ||
|
||
**options** | ||
|
||
- @attr public fit: 'none' | 'center' | 'contain' | 'cover' | 'default' | ||
- @attr public shape: 'square' | 'rounded' | 'circular' | ||
|
||
### Slots | ||
|
||
1 slot for developer to add <img/> element. | ||
|
||
## Accessibility | ||
|
||
The image element requires an alt tag when not used in role: presentation. | ||
|
||
## Preparation | ||
|
||
This will extend the FASTElement. | ||
|
||
Open GitHub issues related to Image component | ||
|
||
- [Feature request](https://github.com/microsoft/fluentui/issues/26452) | ||
- [Bug](https://github.com/microsoft/fluentui/issues/26399) | ||
|
||
## Implementation | ||
|
||
### CSS Guidance | ||
|
||
- [x] Uses design tokens for styling | ||
|
||
An optional border-radius can be expressed using the following design tokens: | ||
|
||
- borderRadiusSmall, | ||
- borderRadiusMedium, | ||
- borderRadiusLarge | ||
- borderRadiusXLarge | ||
|
||
An optional 16px margin can be added to the image to separate it from surrounding content. |
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,230 @@ | ||
import { html } from '@microsoft/fast-element'; | ||
import type { Args, Meta } from '@storybook/html'; | ||
import { renderComponent } from '../helpers.stories.js'; | ||
import type { Image as FluentImage } from './image.js'; | ||
import { ImageFit, ImageShape } from './image.options.js'; | ||
import './define.js'; | ||
|
||
type ImageStoryArgs = Args & FluentImage; | ||
type ImageStoryMeta = Meta<ImageStoryArgs>; | ||
|
||
const imageTemplate = html<ImageStoryArgs>` | ||
<div style="padding: 48px 24px; background-color: rgb(250, 250, 250);"> | ||
<fluent-image | ||
?block=${x => x.block} | ||
?bordered=${x => x.bordered} | ||
fit=${x => x.fit} | ||
?shadow=${x => x.shadow} | ||
shape=${x => x.shape} | ||
> | ||
<img alt="Short image description" src="https://via.placeholder.com/300x100/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
|
||
export default { | ||
title: 'Components/Image', | ||
args: { | ||
block: false, | ||
bordered: false, | ||
shadow: false, | ||
fit: ImageFit.default, | ||
shape: ImageShape.square, | ||
}, | ||
argTypes: { | ||
alt: { | ||
description: 'Alternate text description -- to be supplied by component consumer', | ||
table: { | ||
type: { | ||
summary: | ||
'Required. Alt tag provides text attribution for images. Should be brief but accurate—one or two sentences that describe the image and its context. If the image represents a function, be sure to indicate that. If it’s meant to be consumed with other objects on the page, consider that as well. Don’t repeat information that’s on the page in alt text since screen readers will read it twice.', | ||
}, | ||
}, | ||
}, | ||
block: { | ||
description: | ||
'An image can use the argument ‘block’ so that it’s width will expand to fiill the available container space.', | ||
table: { | ||
defaultValue: { | ||
summary: false, | ||
}, | ||
}, | ||
}, | ||
bordered: { | ||
description: 'Border surrounding image', | ||
table: { | ||
type: { | ||
summary: 'Use this option to provide minimal visual separation between image and surrounding content.', | ||
}, | ||
defaultValue: { | ||
summary: false, | ||
}, | ||
}, | ||
}, | ||
fit: { | ||
description: 'Determines how the image will be scaled and positioned within its parent container.', | ||
table: { | ||
defaultValue: { | ||
summary: 'default', | ||
}, | ||
}, | ||
options: Object.values(ImageFit), | ||
control: 'select', | ||
}, | ||
role: { | ||
description: 'Aria role -- to be supplied by component consumer', | ||
table: { | ||
type: { | ||
summary: | ||
'If images are solely decorative and don’t provide useful information or context, use role=”presentation” to hide them from assistive technologies.', | ||
}, | ||
}, | ||
}, | ||
shadow: { | ||
description: 'Apply an optional box shadow to further separate the image from the background.', | ||
table: { | ||
type: { | ||
summary: | ||
'To give an image additional prominence, use the shadow prop to make it appear elevated. Too many shadows can cause a busy layout, so use them sparingly.', | ||
}, | ||
defaultValue: { | ||
summary: false, | ||
}, | ||
}, | ||
}, | ||
shape: { | ||
description: 'Image shape', | ||
table: { | ||
defaultValue: { | ||
summary: 'square', | ||
}, | ||
}, | ||
options: Object.values(ImageShape), | ||
control: 'select', | ||
}, | ||
src: { | ||
description: 'Image source -- to be supplied by component consumer', | ||
table: { | ||
type: { | ||
summary: 'Required', | ||
}, | ||
}, | ||
}, | ||
}, | ||
} as ImageStoryMeta; | ||
|
||
export const Image = renderComponent(imageTemplate).bind({}); | ||
|
||
// Block layout | ||
const imageLayoutBlock = html<ImageStoryArgs>` | ||
<div style="border: 1px dotted #43ED35;"> | ||
<fluent-image block bordered> | ||
<img role="presentation" src="https://via.placeholder.com/958x20/ddd.png" /> | ||
<img role="presentation" src="https://via.placeholder.com/100x100/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const BlockLayout = renderComponent(imageLayoutBlock).bind({}); | ||
|
||
// Fit: None | ||
const imageFitNoneLarge = html<ImageStoryArgs>` | ||
<div style="height: 150px; width: 300px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="none"> | ||
<img role="presentation" src="https://via.placeholder.com/600x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitNoneLarge = renderComponent(imageFitNoneLarge).bind({}); | ||
|
||
const imageFitNoneSmall = html<ImageStoryArgs>` | ||
<div style="height: 150px; width: 300px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="none"> | ||
<img alt="200x100 placeholder" src="https://via.placeholder.com/200x100/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitNoneSmall = renderComponent(imageFitNoneSmall).bind({}); | ||
|
||
// Fit: Center | ||
const imageFitCenterLarge = html<ImageStoryArgs>` | ||
<div style="height: 210px; width: 650px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="center"> | ||
<img role="presentation" src="https://via.placeholder.com/600x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitCenterLarge = renderComponent(imageFitCenterLarge).bind({}); | ||
|
||
const imageFitCenterSmall = html<ImageStoryArgs>` | ||
<div style="height: 210px; width: 650px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="center"> | ||
<img alt="image layout story" src="https://via.placeholder.com/200x100/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitCenterSmall = renderComponent(imageFitCenterSmall).bind({}); | ||
|
||
const imageFitContain = html<ImageStoryArgs>` | ||
<div style="height: 200px; width: 400px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="contain"> | ||
<img alt="image layout story" src="https://via.placeholder.com/400x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitContain = renderComponent(imageFitContain).bind({}); | ||
|
||
const imageFitContainTall = html<ImageStoryArgs>` | ||
<div style="height: 250px; width: 400px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="contain"> | ||
<img alt="image layout story" src="https://via.placeholder.com/400x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitContainTall = renderComponent(imageFitContainTall).bind({}); | ||
|
||
const imageFitContainWide = html<ImageStoryArgs>` | ||
<div style="height: 200px; width: 450px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="contain"> | ||
<img alt="image layout story" src="https://via.placeholder.com/400x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitContainWide = renderComponent(imageFitContainWide).bind({}); | ||
|
||
// Fit: Cover | ||
const imageFitCoverSmall = html<ImageStoryArgs>` | ||
<div style="height: 200px; width: 400px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="cover"> | ||
<img alt="image layout story" src="https://via.placeholder.com/400x250/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitCoverSmall = renderComponent(imageFitCoverSmall).bind({}); | ||
|
||
const imageFitCoverMedium = html<ImageStoryArgs>` | ||
<div style="height: 200px; width: 400px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="cover"> | ||
<img alt="image layout story" src="https://via.placeholder.com/400x300/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitCoverMedium = renderComponent(imageFitCoverMedium).bind({}); | ||
|
||
const imageFitCoverLarge = html<ImageStoryArgs>` | ||
<div style="height: 200px; width: 400px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="cover"> | ||
<img alt="image layout story" src="https://via.placeholder.com/600x200/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitCoverLarge = renderComponent(imageFitCoverLarge).bind({}); | ||
|
||
// Fit: Default | ||
const imageFitDefault = html<ImageStoryArgs>` | ||
<div style="height: 210px; width: 650px; border: 1px dotted #43ED35;"> | ||
<fluent-image bordered fit="default"> | ||
<img alt="image layout story" src="https://via.placeholder.com/150/ddd.png" /> | ||
</fluent-image> | ||
</div> | ||
`; | ||
export const ImageFitDefault = renderComponent(imageFitDefault).bind({}); |
Oops, something went wrong.