Skip to content

Commit

Permalink
Eui image focus states (#2287)
Browse files Browse the repository at this point in the history
* Improving structure

* Adding hover and focus styles

* Updating changelog

* BEM

* Focus and full screen hover styles

* Adding a11y improvements

* Reverting to old sctructure without a11y

* No caption transition

* typos
  • Loading branch information
elizabetdev authored Sep 16, 2019
1 parent d338f93 commit 2a25854
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 58 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Created `EuiSuggest` component ([#2270](https://github.com/elastic/eui/pull/2270))
- Added missing `compressed` styling to `EuiSwitch` ([#2327](https://github.com/elastic/eui/pull/2327))
- Migrate `EuiBottomBar`, `EuiHealth` and `EuiImage` to TS ([#2328](https://github.com/elastic/eui/pull/2328))
- Added hover and focus states when `allowFullScreen` is true in `EuiImage`([#2287](https://github.com/elastic/eui/pull/2287))
- Converted `EuiColorPicker` to TypeScript ([#2340](https://github.com/elastic/eui/pull/2340))
- Added inline rendering option to `EuiColorPicker` ([#2340](https://github.com/elastic/eui/pull/2340))

Expand Down
24 changes: 23 additions & 1 deletion src/components/image/__snapshots__/image.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiImage is rendered 1`] = `
<figure
aria-label="aria-label"
class="euiImage euiImage--large testClass1 testClass2"
data-test-subj="test subject string"
>
<img
alt="alt"
class="euiImage__img"
src="/cat.jpg"
/>
</figure>
`;

exports[`EuiImage is rendered and allows full screen 1`] = `
<button
class="euiImage euiImage--large euiImage--allowFullScreen testClass1 testClass2"
type="button"
>
<figure
aria-label="aria-label"
class="euiImage euiImage--large testClass1 testClass2"
data-test-subj="test subject string"
>
<img
alt="alt"
class="euiImage__img"
src="/cat.jpg"
/>
<svg
class="euiIcon euiIcon--medium euiIcon--ghost euiIcon-isLoading euiImage__icon"
focusable="false"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</figure>
</button>
`;
65 changes: 54 additions & 11 deletions src/components/image/_image.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,40 @@
}
}

&.euiImage--allowFullScreen:hover {
.euiImage__img {
cursor: pointer;
&.euiImage--allowFullScreen {
&:focus .euiImage__img {
outline: 2px solid $euiFocusRingColor;
}

.euiImage__icon {
&:hover .euiImage__icon {
visibility: visible;
opacity: 1;
fill-opacity: 1;
}

&:hover .euiImage__caption,
&:focus .euiImage__caption {
text-decoration: underline;
}

&:not(.euiImage--hasShadow):hover,
&:not(.euiImage--hasShadow):focus {
.euiImage__img {
@include euiBottomShadowMedium;
}
}

&.euiImage--hasShadow:hover,
&.euiImage--hasShadow:focus {
.euiImage__img {
@include euiBottomShadow;
}
}

.euiImage__img {
cursor: pointer;

// transition the shadow
transition: all $euiAnimSpeedFast $euiAnimSlightResistance;
}
}

Expand Down Expand Up @@ -62,31 +88,48 @@

.euiImage__icon {
visibility: hidden;
opacity: 0;
fill-opacity: 0;
position: absolute;
right: $euiSize;
top: $euiSize;
transition: opacity $euiAnimSpeedSlow $euiAnimSlightResistance ;
transition: fill-opacity $euiAnimSpeedSlow $euiAnimSlightResistance;
cursor: pointer;
}

// The FullScreen image that optionally pops up on click.
.euiImageFullScreen {
.euiImage-isFullScreen {
position: relative;
max-height: 80vh;
max-width: 80vw;
animation: euiImageFullScreen $euiAnimSpeedExtraSlow $euiAnimSlightBounce;

.euiImageFullScreen__img {
.euiImage-isFullScreen__icon {
position: absolute;
right: $euiSize;
top: $euiSize;
}

.euiImage-isFullScreen__img {
max-height: 80vh;
max-width: 80vw;
cursor: pointer;
transition: all $euiAnimSpeedFast $euiAnimSlightResistance;
}

&:hover .euiImageFullScreen__img {
&:hover .euiImage-isFullScreen__img {
@include euiBottomShadow;
cursor: pointer;
}
}

&:focus .euiImage-isFullScreen__img {
outline: 2px solid $euiFocusRingColor;
}

&:hover .euiImage__caption,
&:focus-within .euiImage__caption {
text-decoration: underline;
}
}

@keyframes euiImageFullScreen {
0% {
Expand Down
14 changes: 14 additions & 0 deletions src/components/image/image.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,18 @@ describe('EuiImage', () => {

expect(component).toMatchSnapshot();
});

test('is rendered and allows full screen', () => {
const component = render(
<EuiImage
alt="alt"
size="l"
url="/cat.jpg"
allowFullScreen
{...requiredProps}
/>
);

expect(component).toMatchSnapshot();
});
});
95 changes: 49 additions & 46 deletions src/components/image/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ interface EuiImageProps extends CommonProps, HTMLAttributes<HTMLElement> {
}

interface State {
isFullScreen: boolean;
isFullScreenActive: boolean;
}

export class EuiImage extends Component<EuiImageProps, State> {
state: State = {
isFullScreen: false,
isFullScreenActive: false,
};

onKeyDown = (event: React.KeyboardEvent) => {
Expand All @@ -59,13 +59,13 @@ export class EuiImage extends Component<EuiImageProps, State> {

closeFullScreen = () => {
this.setState({
isFullScreen: false,
isFullScreenActive: false,
});
};

openFullScreen = () => {
this.setState({
isFullScreen: true,
isFullScreenActive: true,
});
};

Expand All @@ -82,6 +82,8 @@ export class EuiImage extends Component<EuiImageProps, State> {
...rest
} = this.props;

const { isFullScreenActive } = this.state;

const classes = classNames(
'euiImage',
sizeToClassNameMap[size],
Expand All @@ -99,54 +101,55 @@ export class EuiImage extends Component<EuiImageProps, State> {
);
}

let optionalIcon;
const allowFullScreenIcon = (
<EuiIcon
type="fullScreen"
color={fullScreenIconColorMap[fullScreenIconColor]}
className="euiImage__icon"
/>
);

if (allowFullScreen) {
optionalIcon = (
<EuiIcon
type="fullScreen"
color={fullScreenIconColorMap[fullScreenIconColor]}
className="euiImage__icon"
/>
);
}
const fullScreenDisplay = (
<EuiOverlayMask onClick={this.closeFullScreen}>
<EuiFocusTrap clickOutsideDisables={true}>
<button
type="button"
className="euiImage-isFullScreen"
onClick={this.closeFullScreen}
onKeyDown={this.onKeyDown}>
<figure>
<img src={url} className="euiImage-isFullScreen__img" alt={alt} />
{optionalCaption}

<EuiIcon
type="cross"
color={fullScreenIconColorMap[fullScreenIconColor]}
className="euiImage-isFullScreen__icon"
/>
</figure>
</button>
</EuiFocusTrap>
</EuiOverlayMask>
);

let fullScreenDisplay;

if (this.state.isFullScreen) {
fullScreenDisplay = (
<EuiOverlayMask onClick={this.closeFullScreen}>
<EuiFocusTrap clickOutsideDisables={true}>
<button
type="button"
onClick={this.closeFullScreen}
onKeyDown={this.onKeyDown}>
<figure className="euiImageFullScreen">
<img src={url} className="euiImageFullScreen__img" alt={alt} />
{optionalCaption}
</figure>
</button>
</EuiFocusTrap>
</EuiOverlayMask>
if (allowFullScreen) {
return (
<button type="button" onClick={this.openFullScreen} className={classes}>
<figure {...rest}>
<img src={url} className="euiImage__img" alt={alt} />
{optionalCaption}
{allowFullScreenIcon}
{isFullScreenActive && fullScreenDisplay}
</figure>
</button>
);
}

return (
<button
type="button"
onClick={allowFullScreen ? this.openFullScreen : undefined}>
} else {
return (
<figure className={classes} {...rest}>
<img src={url} className="euiImage__img" alt={alt} />
{optionalCaption}

{/*
If the below fullScreen image renders, it actually attaches to the body because of
EuiOverlayMask's React portal usage.
*/}
{optionalIcon}
{fullScreenDisplay}
</figure>
</button>
);
);
}
}
}

0 comments on commit 2a25854

Please sign in to comment.