Skip to content

Commit

Permalink
Merge pull request google#5544 from google/enhancement/google#5459-im…
Browse files Browse the repository at this point in the history
…age-radio

Add an ImageRadio component.
  • Loading branch information
tofumatt authored Jul 21, 2022
2 parents bdfb8c1 + eed4712 commit 1f18091
Show file tree
Hide file tree
Showing 5 changed files with 375 additions and 0 deletions.
124 changes: 124 additions & 0 deletions assets/js/components/ImageRadio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* ImageRadio component.
*
* Site Kit by Google, Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import PropTypes from 'prop-types';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useCallback } from '@wordpress/element';

/**
* Internal dependencies
*/
import { MDCFormField, MDCRadio } from '../material-components';

const ImageRadio = ( {
onClick,
id,
name,
value,
checked,
tabIndex,
onKeyDown,
image,
label,
children,
description,
ariaLabel,
} ) => {
const formFieldRef = useCallback( ( element ) => {
if ( element !== null ) {
const formField = new MDCFormField( element );
const radioElement = element.querySelector(
'.image-radio.mdc-radio'
);

if ( radioElement ) {
formField.input = new MDCRadio( radioElement );
}
}
}, [] );

return (
<div
className="mdc-form-field googlesitekit-image-radio"
ref={ formFieldRef }
>
<div className="image-radio mdc-radio">
<input
aria-label={ ariaLabel ? ariaLabel : label }
className="mdc-radio__native-control"
onClick={ onClick }
onKeyDown={ onKeyDown }
type="radio"
id={ id }
name={ name }
value={ value }
checked={ checked }
tabIndex={ tabIndex }
/>
<div className="mdc-image-radio__background">
<div
role="radio"
aria-checked={ checked }
className={ classnames( 'mdc-image-radio__content', {
'mdc-image-radio__content--no-image': ! image,
} ) }
>
{ image ? image : label }
</div>
</div>
</div>
<label htmlFor={ id }>
{ image && <span>{ label }</span> }
{ description ? description : children }
</label>
</div>
);
};

ImageRadio.propTypes = {
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
checked: PropTypes.bool,
tabIndex: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ),
label: PropTypes.string.isRequired,
children: PropTypes.string.isRequired,
image: PropTypes.element,
description: PropTypes.string,
ariaLabel: PropTypes.string,
};

ImageRadio.defaultProps = {
onClick: null,
onKeyDown: null,
checked: false,
disabled: false,
tabIndex: undefined,
label: '',
};

export default ImageRadio;
1 change: 1 addition & 0 deletions assets/sass/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
@import "components/global/googlesitekit-tooltip";
@import "components/global/googlesitekit-use-snippet-switch";
@import "components/global/googlesitekit-view-only-menu";
@import "components/global/googlesitekit-image-radio";

// Dashboard
@import "components/dashboard/googlesitekit-signin-box";
Expand Down
121 changes: 121 additions & 0 deletions assets/sass/components/global/_googlesitekit-image-radio.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
.googlesitekit-image-radio {
display: flex;
flex-direction: column;
padding: 0.625rem;
width: fit-content;

& > label {
color: $c-user-input-note;
font-size: 0.75rem;
margin-top: 0.625rem;
padding: 0;

span {
color: $c-neutral-n-900;
display: block;
font-size: 0.875rem;
margin-bottom: 0.125rem;
}
}

.image-radio {

/**
* The material radio button has an inline ripple size property,
* that is used for the animation. The !important here is to override this,
* and disabling the animation by setting the ripple size to zero.
*/
--mdc-ripple-fg-size: 0 !important;

height: unset;
padding: 0;
width: 100%;

.mdc-image-radio__background {

.mdc-image-radio__content {
align-items: center;
background-color: $c-surfaces-surface;
border: 0.3125rem solid $c-surfaces-surface;
border-radius: 0.5625rem;
color: $c-neutral-n-900;
display: flex;
height: 12.1875rem;
justify-content: center;
outline: 0.0625rem solid $c-jumbo;
width: 16.5rem;

&--no-image {
background-color: $c-surfaces-surface-2;
height: 5.0625rem;
text-align: center;
}

svg {
object-fit: contain;
width: 100%;
}
}
}

&:hover .mdc-image-radio__content {
box-shadow: 0 0 5px 5px $c-surfaces-surface-2;
position: relative;

&::after {
background-color: $c-radio-background-hover;
border-radius: 0.5625rem;
content: "";
height: calc(100% + 0.75rem);
left: 50%;
opacity: 0.2;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: calc(100% + 0.75rem);
}

&--no-image {
background-color: $c-radio-background-hover;

&::after {
content: unset;
}
}
}

&.mdc-ripple-upgraded--background-focused .mdc-image-radio__content {
box-shadow: 0 0 5px 5px $c-surfaces-surface-2;
position: relative;

&::after {
background-color: $c-radio-background-focus;
border-radius: 0.5625rem;
content: "";
height: calc(100% + 0.75rem);
left: 50%;
opacity: 0.2;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: calc(100% + 0.75rem);
}

&--no-image {
background-color: $c-radio-background-focus;

&::after {
content: unset;
}
}
}

.mdc-radio__native-control[checked] + .mdc-image-radio__background {

.mdc-image-radio__content {
border: 0.3125rem solid $c-surfaces-surface;
outline: 0.125rem solid $c-content-link;
}
}
}
}
3 changes: 3 additions & 0 deletions assets/sass/config/_variables-mui3.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ $c-surfaces-on-surface-variant: #5f6561;
$c-surfaces-surface: #fff;
$c-surfaces-surface-1: #ebeef0;
$c-surfaces-surface-2: #cbd0d3;
$c-surfaces-surface-3: #ebeef0;
$c-surfaces-inverse-on-surface: #ebeef0;
$c-surfaces-inverse-surface: #161b18;
$c-content-on-primary: #fff;
Expand Down Expand Up @@ -168,6 +169,8 @@ $c-interactive-tertiary-press: rgba(22, 27, 24, 0.16);
// Custom color not from palette.
$c-content-link: #108080;
$c-content-primary-hover: #2e5f41;
$c-radio-background-hover: #aeb3b5;
$c-radio-background-focus: #8a8f90;

// Font sizes
$fs-display-lg: 58px;
Expand Down
126 changes: 126 additions & 0 deletions stories/imageradio.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* Radio Component Stories.
*
* Site Kit by Google, Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import { storiesOf } from '@storybook/react';

/**
* Internal dependencies
*/
import ImageRadio from '../assets/js/components/ImageRadio';

const image = (
<svg
width="241"
height="175"
viewBox="0 0 241 175"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<mask
id="mask0_1205_5766"
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="241"
height="175"
>
<rect width="241" height="175" rx="5" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_1205_5766)">
<rect opacity="0.5" width="241" height="65" fill="#EBEEF0" />
<rect
opacity="0.5"
x="165"
y="71"
width="76"
height="104"
fill="#EBEEF0"
/>
<rect opacity="0.5" y="71" width="159" height="21" fill="#EBEEF0" />
<rect opacity="0.5" y="98" width="159" height="21" fill="#EBEEF0" />
<rect
opacity="0.5"
y="126"
width="159"
height="21"
fill="#EBEEF0"
/>
<rect
opacity="0.5"
y="154"
width="159"
height="21"
fill="#EBEEF0"
/>
</g>
</svg>
);

storiesOf( 'Global', module ).add( 'Image Radios', () => (
<div>
<div>
<ImageRadio
id="image-radio-story"
name="image-radio-story"
value="story"
image={ image }
label="Image Radio"
description="This is a description"
>
Default
</ImageRadio>
<ImageRadio
id="image-radio-story"
name="image-radio-story"
value="story"
image={ image }
label="Image Radio"
description="This is a description"
checked
>
Checked
</ImageRadio>
</div>
<div>
<ImageRadio
id="image-radio-story"
name="image-radio-story"
value="story"
label="Image Radio"
description="This is a description"
>
Without image
</ImageRadio>
</div>
<div>
<ImageRadio
id="image-radio-story"
name="image-radio-story"
value="story"
label="Image Radio"
description="This is a description"
checked
>
Checked, without image
</ImageRadio>
</div>
</div>
) );

0 comments on commit 1f18091

Please sign in to comment.