-
Notifications
You must be signed in to change notification settings - Fork 827
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2809 from salesforce-ux/feat/carousel
feat(carousel): Add carousel component
- Loading branch information
Showing
20 changed files
with
775 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 |
---|---|---|
|
@@ -14,6 +14,7 @@ const elements = [ | |
'li', | ||
'code', | ||
'blockquote', | ||
'pre', | ||
'tr', | ||
'td', | ||
'th', | ||
|
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 |
---|---|---|
|
@@ -56,6 +56,7 @@ h6.doc { | |
|
||
p.doc { | ||
font-size: 1rem; | ||
margin-top: 1.5rem; | ||
margin-bottom: 1.5rem; | ||
} | ||
|
||
|
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
46 changes: 46 additions & 0 deletions
46
ui/components/carousel/__tests__/__snapshots__/renders_a_carousel_auto_play_disabled.json
Large diffs are not rendered by default.
Oops, something went wrong.
46 changes: 46 additions & 0 deletions
46
ui/components/carousel/__tests__/__snapshots__/renders_a_carousel_auto_play_enabled.json
Large diffs are not rendered by default.
Oops, something went wrong.
41 changes: 41 additions & 0 deletions
41
ui/components/carousel/__tests__/__snapshots__/renders_a_carousel_first_tab_active.json
Large diffs are not rendered by default.
Oops, something went wrong.
41 changes: 41 additions & 0 deletions
41
ui/components/carousel/__tests__/__snapshots__/renders_a_carousel_second_tab_active.json
Large diffs are not rendered by default.
Oops, something went wrong.
41 changes: 41 additions & 0 deletions
41
ui/components/carousel/__tests__/__snapshots__/renders_a_carousel_third_tab_active.json
Large diffs are not rendered by default.
Oops, something went wrong.
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,21 @@ | ||
/* eslint-env jest */ | ||
import React from 'react'; | ||
import Carousel from '../'; | ||
import createHelpers from '../../../../jest.setup'; | ||
|
||
const { matchesMarkupAndStyle } = createHelpers(__dirname); | ||
|
||
it('renders a carousel, first tab active', () => | ||
matchesMarkupAndStyle(<Carousel panelActive="1" />)); | ||
|
||
it('renders a carousel, second tab active', () => | ||
matchesMarkupAndStyle(<Carousel panelActive="2" />)); | ||
|
||
it('renders a carousel, third tab active', () => | ||
matchesMarkupAndStyle(<Carousel panelActive="3" />)); | ||
|
||
it('renders a carousel, auto-play enabled', () => | ||
matchesMarkupAndStyle(<Carousel panelActive="1" autoPlay />)); | ||
|
||
it('renders a carousel, auto-play disabled', () => | ||
matchesMarkupAndStyle(<Carousel panelActive="1" autoPlay="stop" />)); |
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,45 @@ | ||
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved | ||
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license | ||
|
||
/** | ||
* A Carousel can accept a maximum number of 5 panels where only 1 panel is visible at a time. | ||
* | ||
* You are able to navigate between panels but interacting with the `slds-carousel__indicator` elements that sit below the panel. | ||
* | ||
* A panel becomes visible by toggling the `slds-hide` class with the `slds-show` class on the `slds-carousel__panel` element. | ||
* | ||
* When making the `slds-carousel__panel` active, the indicator should be updated with the `slds-is-active` class. This provides visual feedback showing which carousel panel is active. | ||
* | ||
* ### Accessibility | ||
* | ||
* A Carousel is built using a tabbed UI specification and requires the following to meet accessibility requirements: | ||
* | ||
* - The tab list, which should have `role="tablist"` | ||
* - The tabs in that list, which should each be an `<a role="tab">` anchor wrapped in a `<li role="presentation">` list item | ||
* - The tab panels, which display each tab’s content and should each have `role="tabpanel"` | ||
* | ||
* **Expected markup:** | ||
* | ||
* - Selected tab’s anchor has `aria-selected="true"`, all other tabs’ anchors have `aria-selected="false"` | ||
* - Selected tab’s anchor has `tabindex="0"`, all other tabs have `tabindex="-1"` | ||
* - Each tab’s anchor has an `aria-controls` attribute whose value is the id of the associated `<div role="tabpanel">` | ||
* - Each tab panel has an `aria-labelledby` attribute whose value is the id of its associated `<a role="tab">` | ||
* - When the Carousel is set to auto-play, the HTML for the pause button is required to precede the HTML of the tab set | ||
* | ||
* **Expected keyboard interactions:** | ||
* | ||
* - Arrow keys, when focus is on selected tab, cycle selection to the next or previous tab | ||
* - Tab key, when focus is before the tab list, moves focus to the selected tab panel | ||
* - Tab key, when focus is on selected tabpanel, moves focus into the selected tabpanel's associated tab or to the next focusable element on the page if that panel has no focusable elements | ||
* - Shift+Tab keys, when focus is on first element in a tab panel, move focus focus entirely from tabset | ||
* | ||
* @summary A carousel allows multiple pieces of featured content to occupy an allocated amount of space. | ||
* | ||
* @base | ||
* @name carousel | ||
* @selector .slds-carousel | ||
* @support dev-ready | ||
* @category base | ||
* @type navigation | ||
* @layout responsive | ||
*/ |
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,96 @@ | ||
import CodeView from '../../../shared/components/CodeView'; | ||
import Example from '../../../shared/components/Example'; | ||
import Carousel from './'; | ||
|
||
<div className="lead"> | ||
A carousel allows multiple pieces of featured content to occupy an allocated amount of space. | ||
</div> | ||
|
||
<Example> | ||
<CodeView> | ||
<Carousel panelActive="1" autoPlay /> | ||
</CodeView> | ||
</Example> | ||
|
||
A Carousel can accept a maximum number of 5 panels where only 1 panel is visible at a time. | ||
|
||
## Navigating between panels | ||
|
||
You are able to navigate between panels but interacting with the `slds-carousel__indicator` elements that sit below the panel. | ||
|
||
<Example> | ||
<div className="slds-grid slds-grid_pull-padded"> | ||
<div className="slds-size_1-of-3 slds-p-horizontal_medium"> | ||
<Carousel panelActive="1" /> | ||
</div> | ||
<div className="slds-size_1-of-3 slds-p-horizontal_medium"> | ||
<Carousel panelActive="2" /> | ||
</div> | ||
</div> | ||
</Example> | ||
|
||
A panel becomes visible by toggling the `slds-hide` class with the `slds-show` class on the `slds-carousel__panel` element. | ||
|
||
```html | ||
<div id="content-id-01" class="slds-carousel__panel slds-show" role="tabpanel" aria-labelledby="indicator-id-01"> | ||
... | ||
</div> | ||
<div id="content-id-02" class="slds-carousel__panel slds-hide" role="tabpanel" aria-labelledby="indicator-id-02"> | ||
... | ||
</div> | ||
``` | ||
|
||
When making the `slds-carousel__panel` active, the indicator should be updated with the `slds-is-active` class. This provides visual feedback showing which carousel panel is active. | ||
|
||
## Auto-play | ||
|
||
<Example> | ||
<Carousel panelActive="1" autoPlay /> | ||
</Example> | ||
|
||
If the Carousel is set to auto-play, a pause button is required to be first in the HTML before the tab set. | ||
|
||
```html | ||
<div class="slds-carousel__stage"> | ||
|
||
<!-- Pause Button --> | ||
<span class="slds-carousel__autoplay"> | ||
<button class="slds-button slds-button_icon slds-button_icon-border-filled slds-button_icon-xx-small" aria-pressed="false" title="Stop auto-play"> | ||
<svg class="slds-button__icon" aria-hidden="true"> | ||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#pause"></use> | ||
</svg> | ||
<span class="slds-assistive-text">Stop auto-play</span> | ||
</button> | ||
</span> | ||
|
||
<!-- Tabs --> | ||
<div class="slds-carousel__panels"> | ||
... | ||
</div> | ||
... | ||
</div> | ||
``` | ||
|
||
When that pause button is interacted with, the `aria-pressed` role needs to be toggled to `true`. | ||
|
||
### Accessibility | ||
|
||
A Carousel is built using a tabbed UI specification and requires the following to meet accessibility requirements: | ||
|
||
- The tab list, which should have `role="tablist"` | ||
- The tabs in that list, which should each be an `<a role="tab">` anchor wrapped in a `<li role="presentation">` list item | ||
- The tab panels, which display each tab’s content and should each have `role="tabpanel"` | ||
|
||
**Expected markup:** | ||
|
||
- Selected tab’s anchor has `aria-selected="true"`, all other tabs’ anchors have `aria-selected="false"` | ||
- Selected tab’s anchor has `tabindex="0"`, all other tabs have `tabindex="-1"` | ||
- Each tab’s anchor has an `aria-controls` attribute whose value is the id of the associated `<div role="tabpanel">` | ||
- Each tab panel has an `aria-labelledby` attribute whose value is the id of its associated `<a role="tab">` | ||
|
||
**Expected keyboard interactions:** | ||
|
||
- Arrow keys, when focus is on selected tab, cycle selection to the next or previous tab | ||
- Tab key, when focus is before the tab list, moves focus to the selected tab panel | ||
- Tab key, when focus is on selected tab, moves focus into the selected tab’s associated tab panel or to the next focusable element on the page if that panel has no focusable elements | ||
- Shift+Tab keys, when focus is on first element in a tab panel, move focus to the selected tab |
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,147 @@ | ||
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved | ||
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license | ||
|
||
/** | ||
* @summary Initiates a carousel component | ||
* @name base | ||
* @selector .slds-carousel | ||
* @restrict div | ||
* @variant | ||
*/ | ||
.slds-carousel { | ||
display: flex; | ||
position: relative; | ||
} | ||
|
||
/** | ||
* @summary Main stage for carousel's tab-panels and tab-list inidicators | ||
* @selector .slds-carousel__stage | ||
* @restrict .slds-carousel div | ||
*/ | ||
.slds-carousel__stage { | ||
position: relative; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
/** | ||
* @summary Actionable element that contains the carousel's tab-panel content | ||
* @selector .slds-carousel__panel-action | ||
* @restrict .slds-carousel__stage a | ||
*/ | ||
.slds-carousel__panel-action { | ||
display: block; | ||
border: $border-width-thin solid $color-border; | ||
border-radius: $border-radius-medium; | ||
|
||
&:focus { | ||
@include focus-button; | ||
border-color: $color-border-brand; | ||
outline: 0; | ||
} | ||
} | ||
|
||
/** | ||
* @summary Element that contains the image inside the carousel's tab-panel | ||
* @selector .slds-carousel__image | ||
* @restrict .slds-carousel__panel-action div | ||
*/ | ||
.slds-carousel__image { | ||
border-top-left-radius: $border-radius-medium; | ||
border-top-right-radius: $border-radius-medium; | ||
overflow: hidden; | ||
} | ||
|
||
/** | ||
* @summary Element that contains the content inside the carousel's tab-panel | ||
* @selector .slds-carousel__content | ||
* @restrict .slds-carousel__panel-action div | ||
*/ | ||
.slds-carousel__content { | ||
background: $carousel-color-background; | ||
padding: $spacing-small; | ||
border-bottom-left-radius: $border-radius-medium; | ||
border-bottom-right-radius: $border-radius-medium; | ||
text-align: center; | ||
} | ||
|
||
/** | ||
* @summary Heading element that contains the title of the carousel's tab-panel | ||
* @selector .slds-carousel__content-title | ||
* @restrict .slds-carousel__content h2 | ||
*/ | ||
.slds-carousel__content-title { | ||
font-size: $font-size-heading-small; | ||
font-weight: 600; | ||
} | ||
|
||
/** | ||
* @summary List element that contains the carousel's tab-list inidicators | ||
* @selector .slds-carousel__indicators | ||
* @restrict .slds-carousel ul | ||
*/ | ||
.slds-carousel__indicators { | ||
align-self: center; | ||
margin: $spacing-x-small 0; | ||
} | ||
|
||
/** | ||
* @summary Carousel's tab-list inidicator items | ||
* @selector .slds-carousel__indicator | ||
* @restrict .slds-carousel__indicators li | ||
*/ | ||
.slds-carousel__indicator { | ||
display: inline-flex; | ||
margin: 0 $spacing-xxx-small; | ||
} | ||
|
||
/** | ||
* @summary Actionable element inside of each tab-list indicator | ||
* @selector .slds-carousel__indicator-action | ||
* @restrict .slds-carousel__indicator a | ||
*/ | ||
.slds-carousel__indicator-action { | ||
width: $carousel-indicator-width; | ||
height: $carousel-indicator-width; | ||
background: $carousel-indicator-color-background; | ||
border: $border-width-thin solid $color-border; | ||
border-radius: 50%; | ||
|
||
@include mq-medium-min { | ||
width: ($carousel-indicator-width / 2); | ||
height: ($carousel-indicator-width / 2); | ||
} | ||
|
||
/** | ||
* @summary Active state notifying the tab that its current panel is active | ||
* @selector .slds-is-active | ||
* @restrict .slds-carousel__indicator-action | ||
*/ | ||
&.slds-is-active, | ||
&.slds-is-active:hover { | ||
background: $carousel-indicator-color-background-active; | ||
border-color: $carousel-indicator-color-background-active; | ||
} | ||
|
||
&:hover { | ||
background-color: $carousel-indicator-color-background-hover; | ||
} | ||
|
||
&:focus { | ||
@include focus-button; | ||
background-color: $carousel-indicator-color-background-focus; | ||
border-color: $carousel-indicator-color-background-focus; | ||
outline: 0; | ||
} | ||
} | ||
|
||
/** | ||
* @summary Element that contains the auto-play button icon to toggle on/off | ||
* @selector .slds-carousel__autoplay | ||
* @restrict .slds-carousel__stage div | ||
*/ | ||
.slds-carousel__autoplay { | ||
position: absolute; | ||
left: 0; | ||
bottom: $spacing-x-small; | ||
} |
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,39 @@ | ||
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved | ||
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license | ||
|
||
import React from 'react'; | ||
import Carousel from '../'; | ||
|
||
export const Context = props => ( | ||
<div style={{ height: '420px' }}>{props.children}</div> | ||
); | ||
|
||
export default <Carousel panelActive="1" />; | ||
|
||
export let states = [ | ||
{ | ||
id: 'tab-1-active', | ||
label: 'Tab 1 Active', | ||
element: <Carousel panelActive="1" /> | ||
}, | ||
{ | ||
id: 'tab-2-active', | ||
label: 'Tab 2 Active', | ||
element: <Carousel panelActive="2" /> | ||
}, | ||
{ | ||
id: 'tab-3-active', | ||
label: 'Tab 3 active', | ||
element: <Carousel panelActive="3" /> | ||
}, | ||
{ | ||
id: 'auto-play-enabled', | ||
label: 'Auto-play enabled', | ||
element: <Carousel panelActive="1" autoPlay /> | ||
}, | ||
{ | ||
id: 'auto-play-stopped', | ||
label: 'Auto-play stopped', | ||
element: <Carousel panelActive="1" autoPlay="stop" /> | ||
} | ||
]; |
Oops, something went wrong.