-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch component for charting basic component #24
Changes from all commits
2a9d597
d73218a
6e2beb7
b1d02b7
ab30547
bc83ede
390c6a3
f25af5e
7064cfd
cc22059
6709244
6cbc398
3446c89
686f36c
d8540bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { newE2EPage } from '@stencil/core/testing'; | ||
let page, element, switchElement; | ||
beforeEach(async () => { | ||
page = await newE2EPage(); | ||
await page.setContent('<calcite-switch text="Test switch"></calcite-switch>'); | ||
await page.waitForChanges(); | ||
element = await page.find('calcite-switch'); | ||
switchElement = await page.find('calcite-switch >>> label'); | ||
}); | ||
|
||
describe('calcite-switch', () => { | ||
it('renders', async () => { | ||
expect(element).toHaveClass('hydrated'); | ||
}); | ||
|
||
it('renders text data', async () => { | ||
expect(switchElement.textContent).toEqual(`Test switch`); | ||
}); | ||
|
||
it('renders switch at correct position', async() => { | ||
element.setProperty('position', 'right'); | ||
await page.waitForChanges(); | ||
let pageEl = await page.find('calcite-switch >>> .toggle-switch__track'); | ||
expect( pageEl).toHaveClass('toggle-switch__track--right'); | ||
|
||
element.setProperty('position', 'left'); | ||
await page.waitForChanges(); | ||
expect( pageEl).toHaveClass('toggle-switch__track--left'); | ||
}); | ||
|
||
it('changes the internal state of checked', async () => { | ||
const input = await page.find('calcite-switch >>> input'); | ||
switchElement.click(); | ||
await page.waitForChanges(); | ||
let value = await input.getProperty('checked'); | ||
expect(value).toBe(true); | ||
|
||
switchElement.click(); | ||
await page.waitForChanges(); | ||
value = await input.getProperty('checked'); | ||
expect(value).toBe(false); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// ┌────────────┐ | ||
// │ Switches │ | ||
//└────────────┘ | ||
// ↳ http: //esri.github.io/calcite-web/documentation/components/#switches | ||
// ↳ components → _switches.md | ||
// variables | ||
$switch-width: 36px; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest avoiding using sass variable for any values that are only used once in the CSS. The result will be less code, and you avoid having to do a lookup when you're trying to find what a CSS property is set to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I felt any absolute values or color values to be fed in by consumers if they want different sizes. |
||
$switch-height: 20px; | ||
$switch-bg: #{$lightest-gray}; | ||
$switch-border-color: #{$light-gray}; | ||
$switch-hover-bg: #{$lighter-gray}; | ||
$switch-hover-border-color: #{$light-gray}; | ||
$switch-checked-bg: $blue; | ||
$switch-checked-border-color: $dark-blue; | ||
$switch-focus-shadow: 0 0 4px 2px rgba($light-gray,0.9); | ||
$switch-focus-checked-shadow: 0 0 4px 2px rgba($lighter-gray,0.9); | ||
$switch-focus-destructive-checked-shadow: 0 0 4px 2px rgba($lighter-gray,0.9); | ||
$handle-size: 18px; | ||
$handle-top-distance: -1px; | ||
$handle-edge-distance: -1px; | ||
$handle-active-edge-distance: 1px; | ||
$handle-bg: $white; | ||
$handle-border-color: $darkest-gray; | ||
$handle-shadow: 0 1px 1px 0px rgba($darkest-gray,0.2); | ||
$handle-hover-border-color: #{$blue}; | ||
$handle-hover-shadow: 0 1px 2px 0px rgba($darkest-gray,0.2); | ||
$handle-checked-border-color: $dark-blue; | ||
$handle-checked-shadow: 0 2px 1px 0px rgba($darkest-gray,0.2); | ||
$handle-active-shadow: 0 3px 1px 0px rgba($darkest-gray,0.2); | ||
$switch-destructive-checked-bg: $dark-red ; | ||
$switch-destructive-checked-border-color: $darker-red; | ||
$handle-destructive-hover-border-color: $dark-red; | ||
$handle-destructive-checked-border-color: $darker-red; | ||
$switch-handle-margin: 18px; | ||
|
||
@mixin position-left($left) { | ||
left: $left; | ||
right: auto; | ||
|
||
html[dir="rtl"] & { | ||
left: auto; | ||
right: $left; | ||
} | ||
} | ||
|
||
@mixin position-right($right) { | ||
right: $right; | ||
left: auto; | ||
|
||
html[dir="rtl"] & { | ||
right: auto; | ||
left: $right; | ||
} | ||
} | ||
|
||
// our containing block element | ||
.toggle-switch { | ||
display: block; | ||
position: relative; | ||
cursor: pointer; | ||
-webkit-user-select: none; | ||
patrickarlt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
-moz-user-select: none; | ||
-ms-user-select: none; | ||
user-select: none; | ||
-webkit-tap-highlight-color: transparent; | ||
} | ||
|
||
// track | ||
.toggle-switch__track { | ||
position: relative; | ||
display: inline-block; | ||
vertical-align: top; | ||
width: $switch-width; | ||
height: $switch-height; | ||
top: .05em; | ||
background-color: $switch-bg; | ||
border-radius: 30px; | ||
border: 1px solid $switch-border-color; | ||
|
||
transition: all 0.25s ease; | ||
// handle | ||
&:after { | ||
position: absolute; | ||
display: block; | ||
content: ''; | ||
width: $handle-size; | ||
height: $handle-size; | ||
top: $handle-top-distance; | ||
@include position-left($handle-edge-distance); | ||
background-color: $handle-bg; | ||
border-radius: 30px; | ||
border: 2px solid $handle-border-color; | ||
box-shadow: $handle-shadow; | ||
transition: all 0.25s ease; | ||
} | ||
|
||
// alignment fixes for edge | ||
@supports (-ms-ime-align: auto) { & { top: .1em; }} | ||
patrickarlt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// alignment fixes for ff | ||
@supports (-moz-appearance: none) { & { top: .1em; }} | ||
// alignment fixes for ios | ||
@supports (-webkit-overflow-scrolling: touch) { & { top: .15em; }} | ||
} | ||
|
||
.toggle-switch__input { | ||
@include visually-hidden; | ||
|
||
// hover | ||
&:hover + .toggle-switch__track { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems odd to hover over something visually hidden - perhaps the hover belongs on a visual element? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. visually hidden is not display none instead it is 1 sq px in size. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. But isn't it pretty impossible to get the mouse to hover over on that 1px? |
||
border-color: $switch-hover-border-color; | ||
background-color: $switch-hover-bg; | ||
&:after { | ||
border-color: $handle-hover-border-color; | ||
box-shadow: $handle-hover-shadow; | ||
} | ||
} | ||
// active | ||
&:active + .toggle-switch__track { | ||
box-shadow: $switch-focus-shadow; | ||
&:after { | ||
@include position-left($handle-active-edge-distance); | ||
border-color: $handle-checked-border-color; | ||
box-shadow: $handle-active-shadow; | ||
} | ||
} | ||
// checked | ||
&:checked + .toggle-switch__track { | ||
border-color: $switch-checked-border-color; | ||
background-color: $switch-checked-bg; | ||
&:after { | ||
@include position-right($handle-edge-distance); | ||
border-color: $handle-checked-border-color; | ||
box-shadow: $handle-checked-shadow; | ||
} | ||
} | ||
// checked and active | ||
&:checked:active + .toggle-switch__track { | ||
box-shadow: $switch-focus-checked-shadow; | ||
&:after { | ||
@include position-right($handle-active-edge-distance); | ||
border-color: $handle-checked-border-color; | ||
box-shadow: $handle-active-shadow; | ||
} | ||
} | ||
// unchecked focus | ||
&:focus + .toggle-switch__track { | ||
box-shadow: $switch-focus-shadow; | ||
&:after { border-color: $handle-hover-border-color; } | ||
} | ||
// checked focus | ||
&:checked:focus + .toggle-switch__track { | ||
box-shadow: $switch-focus-checked-shadow; | ||
&:after { border-color: $handle-checked-border-color; } | ||
} | ||
} | ||
|
||
.toggle-switch--destructive { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest you overload classes with modifiers (e.g. Also - seems like this selector should be renamed if the property is renamed. I'm also in favor of naming that describes behavior (which should be universally true) over intent (may/may not be true). |
||
& .toggle-switch__input:hover + .toggle-switch__track:after { border-color: $handle-destructive-hover-border-color; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps I'm missing something, but these ampersands are unnecessary aren't they? |
||
& .toggle-switch__input:active + .toggle-switch__track:after { border-color: $handle-destructive-checked-border-color; } | ||
& .toggle-switch__input:checked:active + .toggle-switch__track { | ||
box-shadow: $switch-focus-destructive-checked-shadow; | ||
&:after { border-color: $handle-destructive-checked-border-color; } | ||
} | ||
& .toggle-switch__input:checked + .toggle-switch__track { | ||
background-color: $switch-destructive-checked-bg; | ||
border-color: $switch-destructive-checked-border-color; | ||
&:after { border-color: $handle-destructive-checked-border-color; } | ||
} | ||
& .toggle-switch__input:focus + .toggle-switch__track:after { | ||
border-color: $handle-destructive-hover-border-color; | ||
} | ||
& .toggle-switch__input:checked:focus + .toggle-switch__track { | ||
box-shadow: $switch-focus-destructive-checked-shadow; | ||
&:after { border-color: $handle-destructive-checked-border-color; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,49 @@ | ||||||
import { Component, h, Prop, State, Event, Element, Host } from "@stencil/core"; | ||||||
import { EventEmitter } from "@stencil/state-tunnel/dist/types/stencil.core"; | ||||||
// import { ENTER } from "../../utils/keys"; | ||||||
|
||||||
@Component({ | ||||||
tag: "calcite-switch", | ||||||
styleUrl: "calcite-switch.scss", | ||||||
shadow: true | ||||||
}) | ||||||
export class CalciteSwitch { | ||||||
@Element() el: HTMLElement; | ||||||
|
||||||
@Prop() checked = false; | ||||||
|
||||||
@Prop() color: "red" | "green" = "green"; | ||||||
|
||||||
@State() switched = this.checked; | ||||||
|
||||||
@Event() switchChange: EventEmitter; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Events names are part of the global DOM scope so this could potentially interfere with another 3rd party component which emits a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I felt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is something I've been wondering about. Wouldn't we want to model this closely to the stock HTML element that it's using? Like just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Paul too, we are already have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paulcpederson @kumarGayu there are 2 problems with just having this event name be
As an example, Ionic prefixes all their event names with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If that is kind of only option left, I am okay in prefixing |
||||||
@Prop() disabled: boolean = false; | ||||||
@Prop() name: string; | ||||||
@Prop() value: string | number | string[]; | ||||||
childInput: any; | ||||||
childOnchange: any; | ||||||
@Prop() focus: any; | ||||||
|
||||||
componentWillLoad(){ | ||||||
this.childInput = this.el.children[0] || (<input type="checkbox" checked={this.switched} disabled={this.disabled} name={this.name} value={this.value}/>); | ||||||
this.childInput.class = this.childInput.class + "toggle-switch__input"; | ||||||
this.el.innerHTML = "" | ||||||
this.childOnchange = this.childInput.onChange; | ||||||
this.childInput.onChange = this.setInputSlot.bind(this); | ||||||
} | ||||||
|
||||||
render() { | ||||||
return ( | ||||||
<Host class={`toggle-switch toggle-switch--${this.color}`} onClick={this.setInputSlot.bind(this)} checked={this.checked} disabled={this.disabled} focus={this.focus} > | ||||||
{ | ||||||
this.childInput && <slot></slot> | ||||||
} | ||||||
<span class={`toggle-switch__track`} /> | ||||||
</Host> | ||||||
); | ||||||
} | ||||||
setInputSlot(event): void { | ||||||
this.childOnchange && this.childOnchange(event); | ||||||
this.switchChange && this.switchChange.emit(this.switched); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be a little clearer to anyone attaching a listener if we passed them an object with a key rather than just a boolean. Maybe something like
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lot of us are having difference of opinion in nomenclature. Initially we thought we will have it similar to HTML tag properties. but now we thought events would clash so thinking of calcite prefix, IMO, we should follow same for all different properties or events. |
||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with others that the label should not be baked into this component. In the event that decision changes, this should probably be named switchLabel to avoid confusion.