Skip to content

Commit 093cc89

Browse files
committed
feat(ui-img): rework Img
BREAKING CHANGE: contains breaking changes due to component using the new theming system INSTUI-4970
1 parent af97f0c commit 093cc89

7 files changed

Lines changed: 535 additions & 10 deletions

File tree

packages/ui-img/package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,18 @@
6767
"default": "./es/exports/a.js"
6868
},
6969
"./v11_7": {
70-
"src": "./src/exports/a.ts",
71-
"types": "./types/exports/a.d.ts",
72-
"import": "./es/exports/a.js",
73-
"require": "./lib/exports/a.js",
74-
"default": "./es/exports/a.js"
70+
"src": "./src/exports/b.ts",
71+
"types": "./types/exports/b.d.ts",
72+
"import": "./es/exports/b.js",
73+
"require": "./lib/exports/b.js",
74+
"default": "./es/exports/b.js"
7575
},
7676
"./latest": {
77-
"src": "./src/exports/a.ts",
78-
"types": "./types/exports/a.d.ts",
79-
"import": "./es/exports/a.js",
80-
"require": "./lib/exports/a.js",
81-
"default": "./es/exports/a.js"
77+
"src": "./src/exports/b.ts",
78+
"types": "./types/exports/b.d.ts",
79+
"import": "./es/exports/b.js",
80+
"require": "./lib/exports/b.js",
81+
"default": "./es/exports/b.js"
8282
}
8383
}
8484
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
describes: Img
3+
---
4+
5+
An accessible image component
6+
7+
```js
8+
---
9+
type: example
10+
---
11+
<Img src={placeholderImage(250, 250)} />
12+
```
13+
14+
### Margin and display
15+
16+
Use the `margin` prop to add space around `<Img />`. Setting the `display` prop to `block` makes
17+
the image a block-level element.
18+
19+
```js
20+
---
21+
type: example
22+
---
23+
<View textAlign="center" as="div">
24+
<Img margin="small" alt="A placeholder image" src={placeholderImage(300, 200)} />
25+
<Img margin="small" src={placeholderImage(200, 200)} />
26+
<Img display="block" margin="small auto" src={placeholderImage(400, 200)} />
27+
</View>
28+
```
29+
30+
### Color overlay
31+
32+
The `overlay` prop accepts parameters for `color`, `opacity`, and `blend`.
33+
34+
```js
35+
---
36+
type: example
37+
---
38+
<View textAlign="center" as="div">
39+
<Img
40+
src={placeholderImage(200, 200)}
41+
overlay={{color: '#0374B5', opacity: 9, blend: 'overlay'}}
42+
alt="A placeholder image"
43+
margin="x-small"
44+
/>
45+
<Img
46+
src={placeholderImage(200, 200)}
47+
overlay={{color: '#0374B5', opacity: 6, blend: 'multiply'}}
48+
alt="A placeholder image"
49+
margin="x-small"
50+
/>
51+
<Img
52+
src={placeholderImage(200, 200)}
53+
overlay={{color: '#0374B5', opacity: 3}}
54+
alt="A placeholder image"
55+
margin="x-small"
56+
/>
57+
</View>
58+
```
59+
60+
### Cover
61+
62+
When the `constrain` prop is set to `cover` the image fills the _full_ width and height of its
63+
containing element, while maintaining the aspect ratio of the source image.
64+
65+
```js
66+
---
67+
type: example
68+
---
69+
<div style={{width: '66%', height: '11rem'}}>
70+
<Img src={avatarSquare} constrain="cover" />
71+
</div>
72+
```
73+
74+
### Contain
75+
76+
When the `constrain` prop is set to `contain` the image fits within the width and height of its
77+
containing element, while maintaining the aspect ratio of the source image.
78+
79+
```js
80+
---
81+
type: example
82+
---
83+
<View as="div" background="primary-inverse" width="200px" height="200px" textAlign="center">
84+
<Img src={avatarPortrait} constrain="contain" />
85+
</View>
86+
```
87+
88+
### Grayscale and blur filters
89+
90+
Please note: these should only be used for presentational effects.
91+
92+
```js
93+
---
94+
type: example
95+
---
96+
<View textAlign="center" as="div">
97+
<Img
98+
withGrayscale
99+
src={avatarSquare}
100+
alt="A placeholder image"
101+
margin="x-small"
102+
/>
103+
<Img
104+
withBlur
105+
src={avatarSquare}
106+
alt="A placeholder image"
107+
margin="x-small"
108+
/>
109+
</View>
110+
```
111+
112+
### Guidelines
113+
114+
```js
115+
---
116+
type: embed
117+
---
118+
<Guidelines>
119+
<Figure recommendation="a11y" title="Accessibility">
120+
<Figure.Item>Contextual images must have alternative text that describes the information or function represented by them</Figure.Item>
121+
<Figure.Item>Decorative images that do not present important content, are used for layout or non-informative purposes, and do not appear within a link do not need to be presented to screen readers. Decorative and spacer images should have null alternative text (alt="")</Figure.Item>
122+
</Figure>
123+
</Guidelines>
124+
```
File renamed without changes.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import { Component } from 'react'
26+
27+
import { View } from '@instructure/ui-view/latest'
28+
import { passthroughProps } from '@instructure/ui-react-utils'
29+
30+
import { withStyle } from '@instructure/emotion'
31+
32+
import generateStyle from './styles'
33+
34+
import { allowedProps } from './props'
35+
import type { ImgProps } from './props'
36+
37+
/**
38+
---
39+
category: components
40+
---
41+
**/
42+
@withStyle(generateStyle)
43+
class Img extends Component<ImgProps> {
44+
static readonly componentId = 'Img'
45+
46+
static allowedProps = allowedProps
47+
static defaultProps = {
48+
alt: '',
49+
display: 'inline-block',
50+
withGrayscale: false,
51+
withBlur: false
52+
}
53+
54+
ref: Element | null = null
55+
56+
handleRef = (el: Element | null) => {
57+
const { elementRef } = this.props
58+
59+
this.ref = el
60+
61+
if (typeof elementRef === 'function') {
62+
elementRef(el)
63+
}
64+
}
65+
66+
componentDidMount() {
67+
this.props.makeStyles?.()
68+
}
69+
70+
componentDidUpdate() {
71+
this.props.makeStyles?.()
72+
}
73+
74+
render() {
75+
const {
76+
src,
77+
alt,
78+
margin,
79+
display,
80+
overlay,
81+
withGrayscale,
82+
withBlur,
83+
constrain,
84+
width,
85+
height,
86+
elementRef,
87+
styles,
88+
loading,
89+
...props
90+
} = this.props
91+
92+
const a11yProps = {
93+
alt: alt || ''
94+
}
95+
96+
const imageProps = {
97+
css: styles?.img,
98+
src,
99+
loading
100+
}
101+
102+
const containerProps = {
103+
...passthroughProps(props),
104+
width,
105+
height,
106+
margin,
107+
display,
108+
elementRef: this.handleRef
109+
}
110+
111+
if (overlay) {
112+
// if a background image is rendered we add the a11y props on the container element
113+
const rootProps = {
114+
...containerProps
115+
}
116+
117+
return (
118+
<View {...rootProps} as="span" css={styles?.container} data-cid="Img">
119+
{/* eslint-disable-next-line jsx-a11y/alt-text*/}
120+
{<img {...imageProps} {...a11yProps} />}
121+
{overlay && <span css={styles?.overlay} />}
122+
</View>
123+
)
124+
} else {
125+
return (
126+
<View
127+
{...containerProps}
128+
{...imageProps}
129+
{...a11yProps}
130+
as="img"
131+
data-cid="Img"
132+
/>
133+
)
134+
}
135+
}
136+
}
137+
138+
export default Img
139+
export { Img }
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import type { ImgTheme, OtherHTMLAttributes } from '@instructure/shared-types'
26+
import type {
27+
Spacing,
28+
WithStyleProps,
29+
ComponentStyle
30+
} from '@instructure/emotion'
31+
32+
type ImgOwnProps = {
33+
src: string
34+
alt?: string
35+
display?: 'inline-block' | 'block'
36+
/**
37+
* Gets passed down to the img component. Same as the native HTML img's loading attribute
38+
*/
39+
loading?: 'eager' | 'lazy'
40+
/**
41+
* Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
42+
* `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
43+
* familiar CSS-like shorthand. For example: `margin="small auto large"`.
44+
*/
45+
margin?: Spacing
46+
/**
47+
* Valid values for `opacity` are `0` - `10`. Valid values for `blend` are
48+
* `normal` (default), `multiply`, `screen`, `overlay`, and `color-burn`.
49+
*/
50+
overlay?: {
51+
color: string
52+
opacity: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
53+
blend?: 'normal' | 'multiply' | 'screen' | 'overlay' | 'color-burn'
54+
}
55+
withGrayscale?: boolean
56+
withBlur?: boolean
57+
constrain?: 'cover' | 'contain'
58+
elementRef?: (element: Element | null) => void
59+
height?: string | number
60+
width?: string | number
61+
}
62+
63+
type PropKeys = keyof ImgOwnProps
64+
65+
type AllowedPropKeys = Readonly<Array<PropKeys>>
66+
67+
type ImgProps = ImgOwnProps &
68+
WithStyleProps<ImgTheme, ImgStyle> &
69+
OtherHTMLAttributes<ImgOwnProps>
70+
71+
type ImgStyle = ComponentStyle<'overlay' | 'container' | 'img'>
72+
const allowedProps: AllowedPropKeys = [
73+
'src',
74+
'alt',
75+
'display',
76+
'loading',
77+
'margin',
78+
'overlay',
79+
'withGrayscale',
80+
'withBlur',
81+
'constrain',
82+
'elementRef',
83+
'height',
84+
'width'
85+
]
86+
87+
export type { ImgProps, ImgStyle }
88+
export { allowedProps }

0 commit comments

Comments
 (0)